#===============================================================================
# GTEx Tissue-Level Modeling Pipeline # Purpose: Tissue-specific quality
prediction using Lasso regression
#===============================================================================
#—————————————————————————— # 1. Setup
#——————————————————————————
# Load data once
if(!exists("merged_gtex")) {
merged_gtex <- readRDS(file.path(data_path, "processed/merged_gtex.rds"))
}
#—————————————————————————— # 2. Within-Group vs Between-Group
Analysis for Single Tissue #——————————————————————————
# Analyze a specific tissue
tissue_name <- "Esophagus"
tissue_data <- merged_gtex[tissue_main == tissue_name]
subtypes <- unique(tissue_data$tissue_sub)
if(length(subtypes) > 1 && nrow(tissue_data) >= 10) {
# Extract and scale features
feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
feature_matrix <- scale(as.matrix(tissue_data[, ..feature_cols]))
# Calculate distance matrix
dist_matrix <- dist(feature_matrix)
# Calculate within-group and between-group distances
within_distances <- list()
between_distances <- list()
for(subtype in subtypes) {
subtype_indices <- which(tissue_data$tissue_sub == subtype)
if(length(subtype_indices) > 1) {
# Within-group distances
subtype_dist <- as.matrix(dist_matrix)[subtype_indices, subtype_indices]
within_distances[[subtype]] <- subtype_dist[upper.tri(subtype_dist)]
# Between-group distances
other_indices <- which(tissue_data$tissue_sub != subtype)
if(length(other_indices) > 0) {
between_dists <- as.matrix(dist_matrix)[subtype_indices, other_indices]
between_distances[[subtype]] <- as.vector(between_dists)
}
}
}
# Calculate summary statistics
all_within <- unlist(within_distances)
all_between <- unlist(between_distances)
similarity_results <- list(
tissue = tissue_name,
subtypes = subtypes,
samples_per_subtype = table(tissue_data$tissue_sub),
summary = data.frame(
comparison = c("Within Subtype", "Between Subtypes"),
mean_distance = c(mean(all_within), mean(all_between)),
median_distance = c(median(all_within), median(all_between))
),
ratio = mean(all_between) / mean(all_within)
)
# PERMANOVA test
perm_test <- adonis2(dist_matrix ~ tissue_sub, data = tissue_data)
statistical_results <- list(
tissue = tissue_name,
permanova = perm_test,
r_squared = perm_test$R2[1],
p_value = perm_test$`Pr(>F)`[1],
significant = perm_test$`Pr(>F)`[1] < 0.05
)
# Create visualizations
pca_result <- prcomp(feature_matrix)
pca_plot <- ggplot(data.frame(PC1 = pca_result$x[,1], PC2 = pca_result$x[,2],
Subtype = tissue_data$tissue_sub),
aes(x = PC1, y = PC2, color = Subtype)) +
geom_point(alpha = 0.7, size = 3) +
theme_minimal() +
labs(title = paste("PCA of", tissue_name, "by Subtype"))
ggsave(file.path(output_path, paste0(tissue_name, "_subtype_pca.pdf")),
pca_plot, width = 10, height = 8, dpi = 300)
# UMAP visualization
set.seed(123)
umap_result <- uwot::umap(feature_matrix, n_neighbors = min(15, nrow(feature_matrix)-1))
umap_plot <- ggplot(data.frame(UMAP1 = umap_result[,1], UMAP2 = umap_result[,2],
Subtype = tissue_data$tissue_sub),
aes(x = UMAP1, y = UMAP2, color = Subtype)) +
geom_point(alpha = 0.7, size = 3) +
theme_minimal() +
labs(title = paste("UMAP of", tissue_name, "by Subtype"))
ggsave(file.path(output_path, paste0(tissue_name, "_subtype_umap.pdf")),
umap_plot, width = 10, height = 8, dpi = 300)
# Save results
saveRDS(similarity_results, file.path(results_path, paste0(tissue_name, "_distance_analysis.rds")))
saveRDS(statistical_results, file.path(results_path, paste0(tissue_name, "_statistical_tests.rds")))
}
#—————————————————————————— # 3. Tissue-Level Lasso Modeling
#——————————————————————————
# Set parameters
k_folds <- 5
targets <- c("SMRIN", "SMATSSCR")
tissues <- unique(merged_gtex$tissue_main)
all_results <- list()
# Process each target
for(target in targets) {
target_results <- list()
message(paste("\nProcessing target:", target))
# Process each tissue
for(tissue_name in tissues) {
message(paste("\nAnalyzing", tissue_name, "for", target))
# Extract and filter data
tissue_data <- merged_gtex[tissue_main == tissue_name]
tissue_data <- tissue_data[!is.na(tissue_data[[target]])]
if(nrow(tissue_data) < 10) {
message(paste("Insufficient samples for", tissue_name))
next
}
# Feature selection
feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
feature_vars <- apply(tissue_data[, ..feature_cols], 2, var)
valid_features <- feature_cols[feature_vars > 1e-10]
# Create cross-validation folds
set.seed(123)
folds <- createFolds(tissue_data[[target]], k = k_folds)
# Cross-validation
fold_results <- list()
all_fold_predictions <- data.frame()
for(i in 1:k_folds) {
# Split data
test_indices <- folds[[i]]
train_data <- tissue_data[-test_indices]
test_data <- tissue_data[test_indices]
# Feature selection - top 5% correlated
feature_correlations <- sapply(valid_features, function(col) {
cor(train_data[[col]], train_data[[target]], use = "complete.obs")
})
top_n_features <- ceiling(length(valid_features) * 0.05)
top_features <- names(sort(abs(feature_correlations), decreasing = TRUE))[1:top_n_features]
# Prepare matrices
x_train <- as.matrix(train_data[, ..top_features])
y_train <- train_data[[target]]
x_train_scaled <- scale(x_train)
x_mean <- attr(x_train_scaled, "scaled:center")
x_sd <- attr(x_train_scaled, "scaled:scale")
# Train model
cv_fit <- cv.glmnet(x_train_scaled, y_train, alpha = 1)
best_lambda <- cv_fit$lambda.min
lasso_model <- glmnet(x_train_scaled, y_train, alpha = 1, lambda = best_lambda)
# Make predictions
x_test <- as.matrix(test_data[, ..top_features])
x_test_scaled <- scale(x_test, center = x_mean, scale = x_sd)
predictions <- predict(lasso_model, x_test_scaled, s = best_lambda)
# Store predictions
fold_predictions <- data.frame(
fold = i,
sample_id = test_data$sample_id,
actual = test_data[[target]],
predicted = as.numeric(predictions)
)
all_fold_predictions <- rbind(all_fold_predictions, fold_predictions)
# Calculate metrics
rmse <- sqrt(mean((predictions - test_data[[target]])^2))
r_value <- cor(predictions, test_data[[target]])
r2 <- cor(predictions, test_data[[target]])^2
mae <- mean(abs(predictions - test_data[[target]]))
fold_results[[i]] <- list(
fold = i,
rmse = rmse,
r = r_value,
r2 = r2,
mae = mae,
lambda = best_lambda,
model = lasso_model,
features = top_features
)
}
# Calculate overall performance
pooled_rmse <- sqrt(mean((all_fold_predictions$predicted - all_fold_predictions$actual)^2))
pooled_r <- cor(all_fold_predictions$predicted, all_fold_predictions$actual) # <-- R, not R²
pooled_r2 <- cor(all_fold_predictions$predicted, all_fold_predictions$actual)^2
pooled_mae <- mean(abs(all_fold_predictions$predicted - all_fold_predictions$actual))
# Count feature selection frequency
all_selected_features <- unlist(lapply(fold_results, function(x) x$features))
feature_counts <- table(all_selected_features)
consistent_features <- names(feature_counts[feature_counts >= 3])
# Store results
tissue_model_result <- list(
tissue = tissue_name,
target = target,
performance = list(
pooled_rmse = pooled_rmse,
pooled_r = pooled_r,
pooled_r2 = pooled_r2,
pooled_mae = pooled_mae
),
fold_results = fold_results,
feature_counts = feature_counts,
consistent_features = consistent_features,
sample_size = nrow(tissue_data),
all_predictions = all_fold_predictions
)
target_results[[tissue_name]] <- tissue_model_result
# Create visualization for good models (using R > 0.55 which is approximately R² > 0.3)
# if(pooled_r2 > 0.3) { # use if R² is required
if(abs(pooled_r) > 0.55) {
scatter_plot <- ggplot(all_fold_predictions, aes(x = actual, y = predicted)) +
geom_point(alpha = 0.6, color = "darkblue", size = 2) +
geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
geom_smooth(method = "lm", se = TRUE, color = "blue") +
labs(
title = paste(tissue_name, "-", target, "Prediction"),
subtitle = paste("R =", round(pooled_r, 3), "RMSE =", round(pooled_rmse, 3)),
# subtitle = paste("R² =", round(pooled_r2, 3), "RMSE =", round(pooled_rmse, 3)), # use if R² is required
x = paste("Actual", target),
y = paste("Predicted", target)
) +
theme_minimal()
ggsave(file.path(output_path, paste0(tissue_name, "_", target, "_scatter.pdf")),
scatter_plot, width = 10, height = 6, dpi = 300)
}
}
all_results[[target]] <- target_results
saveRDS(target_results, file.path(results_path, paste0(target, "_model_results.rds")))
}
G3;
Processing target: SMRIN
gG3;
Analyzing Skin for SMRIN
gG3;
Analyzing Adipose for SMRIN
gG3;
Analyzing Nerve for SMRIN
gG3;
Analyzing Muscle for SMRIN
gG3;
Analyzing Artery for SMRIN
gG3;
Analyzing Heart for SMRIN
g
saveRDS(all_results, file.path(results_path, "all_tissue_models_complete.rds"))
#—————————————————————————— # 4. Model Performance Summary
#——————————————————————————
# Create performance summary
create_performance_summary <- function(results, target_name) {
summary_df <- data.frame()
for(tissue in names(results)) {
tissue_result <- results[[tissue]]
if(is.null(tissue_result)) next
row <- data.frame(
Tissue = tissue,
Samples = tissue_result$sample_size,
RMSE = round(tissue_result$performance$pooled_rmse, 3),
R = round(tissue_result$performance$pooled_r, 3),
# R_squared = round(tissue_result$performance$pooled_r2, 3), # use if R² is required
MAE = round(tissue_result$performance$pooled_mae, 3),
Consistent_Features = length(tissue_result$consistent_features)
)
summary_df <- rbind(summary_df, row)
}
summary_df <- summary_df[order(abs(summary_df$R), decreasing = TRUE),] # Sort by absolute R
summary_df$Performance_Category <- cut(abs(summary_df$R), # Use absolute R for categories
breaks = c(-Inf, 0.316, 0.548, 0.707, Inf),
labels = c("Poor", "Moderate", "Good", "Excellent"))
# summary_df <- summary_df[order(summary_df$R_squared, decreasing = TRUE),] # Sort by R²
# summary_df$Performance_Category <- cut(summary_df$R_squared, # Use R² for categories
# breaks = c(-Inf, 0.1, 0.3, 0.5, Inf),
# labels = c("Poor", "Moderate", "Good", "Excellent"))
return(summary_df)
}
# Create summaries
rin_summary <- create_performance_summary(all_results[["SMRIN"]], "RIN Score")
autolysis_summary <- create_performance_summary(all_results[["SMATSSCR"]], "Autolysis Score")
# Save summaries
write.csv(rin_summary, file.path(results_path, "rin_model_summary.csv"), row.names = FALSE)
write.csv(autolysis_summary, file.path(results_path, "autolysis_model_summary.csv"), row.names = FALSE)
# Create comparison
model_comparison <- merge(
rin_summary[, c("Tissue", "R")],
autolysis_summary[, c("Tissue", "R")],
by = "Tissue",
suffixes = c("_RIN", "_Auto")
)
model_comparison$Better_Model <- ifelse(abs(model_comparison$R_RIN) > abs(model_comparison$R_Auto),
"RIN", "Autolysis")
# Create comparison when if required for R²
# model_comparison <- merge(
# rin_summary[, c("Tissue", "R_squared")],
# autolysis_summary[, c("Tissue", "R_squared")],
# by = "Tissue",
# suffixes = c("_RIN", "_Auto")
# )
# model_comparison$Better_Model <- ifelse(model_comparison$R_squared_RIN > model_comparison$R_squared_Auto,
# "RIN", "Autolysis")
write.csv(model_comparison, file.path(results_path, "model_comparison.csv"), row.names = FALSE)
#—————————————————————————— # 5. Figure 3 - Model Performance
Visualization #——————————————————————————
# Panel A & B: Performance bar plots
create_horizontal_barplot <- function(all_results, score_type, panel_label) {
r_values <- sapply(all_results[[score_type]], function(x) {
if(is.null(x)) return(0)
return(x$performance$pooled_r)
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
})
plot_data <- data.frame(
Tissue = names(r_values),
R = r_values
)[order(-r_values), ]
plot_data$Tissue <- factor(plot_data$Tissue, levels = rev(plot_data$Tissue))
if(score_type == "SMRIN") {
color_gradient <- colorRampPalette(c("#E8F5E8", "#66cdaa", "#2E8B57"))(100)
} else {
color_gradient <- colorRampPalette(c("#FFF2E6", "#FC8D62", "#D94801"))(100)
}
bar_plot <- ggplot(plot_data, aes(x = R, y = Tissue, fill = R)) +
geom_bar(stat = "identity", width = 0.8) +
geom_text(aes(label = sprintf("%.3f", R), x = R/2),
hjust = 0.5, size = 3.5, fontface = "bold") +
scale_fill_gradientn(colors = color_gradient, guide = "none") +
theme_minimal() +
labs(title = panel_label, x = "Correlation Coefficient (R)", y = "") +
theme(
plot.title = element_text(size = 20, face = "bold", hjust = 0),
axis.title.x = element_text(size = 14, face = "bold"),
axis.text.y = element_text(size = 13, face = "bold")
)
return(bar_plot)
}
rin_barplot <- create_horizontal_barplot(all_results, "SMRIN", "A")
autolysis_barplot <- create_horizontal_barplot(all_results, "SMATSSCR", "B")
# Panel C: RIN scatter plots
create_scatter_panel <- function(all_results, top_tissues) {
plots <- list()
for(i in 1:3) {
result <- all_results[["SMRIN"]][[top_tissues[i]]]
if(is.null(result)) next
all_preds <- result$all_predictions
r_value <- result$performance$pooled_r
# r_value <- sqrt(result$performance$pooled_r2) # use when required for R²
rmse_value <- result$performance$pooled_rmse
p <- ggplot(all_preds, aes(x = actual, y = predicted)) +
geom_point(alpha = 0.7, color = "#66cdaa", size = 1.5) +
geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
geom_smooth(method = "lm", se = TRUE, color = "blue") +
ggtitle(paste0(top_tissues[i], " | R = ", round(r_value, 3))) +
theme_minimal()
plots[[i]] <- p
}
return(plots)
}
# Get top 3 tissues
all_r_rin <- sapply(all_results[["SMRIN"]], function(x) {
if(is.null(x)) return(0)
return(abs(x$performance$pooled_r)) # Use absolute value for sorting
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
})
top_rin_tissues <- names(sort(all_r_rin, decreasing = TRUE))[1:3]
rin_scatter_plots <- create_scatter_panel(all_results, top_rin_tissues)
rin_grid <- grid.arrange(grobs = rin_scatter_plots, nrow = 1,
bottom = textGrob("Actual RIN Score", gp = gpar(fontsize = 18, fontface = "bold")),
left = textGrob("Predicted RIN Score", rot = 90, gp = gpar(fontsize = 18, fontface = "bold")),
top = textGrob("C", x = 0.02, just = "left", gp = gpar(fontsize = 18, fontface = "bold")))

# Panel D: Autolysis box plots
create_boxplot_panel <- function(all_results, top_tissues) {
plots <- list()
for(i in 1:3) {
result <- all_results[["SMATSSCR"]][[top_tissues[i]]]
if(is.null(result)) next
all_preds <- result$all_predictions
all_preds$actual_factor <- factor(all_preds$actual, levels = c(0, 1, 2, 3))
r_value <- sqrt(result$performance$pooled_r2)
rmse_value <- result$performance$pooled_rmse
p <- ggplot(all_preds, aes(x = actual_factor, y = predicted)) +
geom_boxplot(aes(fill = actual_factor), outlier.shape = NA) +
geom_jitter(width = 0.2, height = 0, alpha = 0.2, color = "#21908d") +
scale_fill_manual(values = c("0" = "#e0e0e0", "1" = "#bdd7e7",
"2" = "#9ecae1", "3" = "#6baed6")) +
ggtitle(paste0(top_tissues[i], " | R = ", round(r_value, 3))) +
theme_minimal() +
theme(legend.position = "none")
plots[[i]] <- p
}
return(plots)
}
all_r_auto <- sapply(all_results[["SMATSSCR"]], function(x) {
if(is.null(x)) return(0)
return(sqrt(x$performance$pooled_r2))
})
top_auto_tissues <- names(sort(all_r_auto, decreasing = TRUE))[1:3]
auto_boxplot_plots <- create_boxplot_panel(all_results, top_auto_tissues)
autolysis_grid <- grid.arrange(grobs = auto_boxplot_plots, nrow = 1,
bottom = textGrob("Actual Category", gp = gpar(fontsize = 18, fontface = "bold")),
left = textGrob("Predicted Score", rot = 90, gp = gpar(fontsize = 18, fontface = "bold")),
top = textGrob("D", x = 0.02, just = "left", gp = gpar(fontsize = 18, fontface = "bold")))

# Combine all panels
combined_barplots <- grid.arrange(rin_barplot, autolysis_barplot, ncol = 2)

combined_CD_plots <- grid.arrange(rin_grid, autolysis_grid, nrow = 2)

figure3_final <- grid.arrange(combined_barplots, combined_CD_plots, ncol = 2, widths = c(0.45, 0.55))

ggsave(file.path(output_path, "Figure3_Final_Complete.pdf"),
figure3_final, width = 20, height = 12, dpi = 600, bg = "white")
#—————————————————————————— # 6. Figure 4 - Feature Importance
#——————————————————————————
# Extract top features
extract_features <- function(results, target, top_n = 20) {
all_tissue_features <- data.frame()
for(tissue in names(results[[target]])) {
tissue_result <- results[[target]][[tissue]]
if(is.null(tissue_result)) next
if(!is.null(tissue_result$feature_counts)) {
features <- names(tissue_result$feature_counts)
counts <- as.numeric(tissue_result$feature_counts)
tissue_df <- data.frame(
feature = features,
count = counts,
tissue = tissue,
stringsAsFactors = FALSE
)
all_tissue_features <- rbind(all_tissue_features, tissue_df)
}
}
feature_summary <- all_tissue_features %>%
group_by(feature) %>%
summarize(total_count = sum(count)) %>%
arrange(desc(total_count)) %>%
slice(1:top_n)
feature_matrix <- all_tissue_features %>%
filter(feature %in% feature_summary$feature) %>%
reshape2::dcast(feature ~ tissue, value.var = "count", fill = 0)
return(list(summary = feature_summary, matrix = feature_matrix))
}
rin_features <- extract_features(all_results, "SMRIN")
auto_features <- extract_features(all_results, "SMATSSCR")
# Find common features
common_features <- intersect(rin_features$summary$feature, auto_features$summary$feature)
rin_only_features <- setdiff(rin_features$summary$feature, auto_features$summary$feature)
auto_only_features <- setdiff(auto_features$summary$feature, rin_features$summary$feature)
# Create heatmaps
create_heatmap <- function(feature_data, color_palette) {
feature_long <- reshape2::melt(feature_data$matrix, id.vars = "feature",
variable.name = "tissue", value.name = "count")
feature_long <- merge(feature_long, feature_data$summary[, c("feature", "total_count")], by = "feature")
feature_long$feature_label <- sub("feature_", "", feature_long$feature)
p <- ggplot(feature_long, aes(x = tissue, y = reorder(feature_label, total_count))) +
geom_tile(aes(fill = count), color = "white", size = 0.1) +
scale_fill_gradientn(colors = color_palette, name = "Selection\nFrequency") +
labs(x = NULL, y = "Feature ID") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 65, hjust = 1, size = 12, face = "bold"),
axis.text.y = element_text(size = 12, face = "bold"),
panel.grid = element_blank()
)
return(p)
}
rin_heatmap <- create_heatmap(rin_features,
colorRampPalette(c("white", "#66CDAA", "#006400"))(100)) +
ggtitle("A")
auto_heatmap <- create_heatmap(auto_features,
colorRampPalette(c("white", "#FC8D62", "#D94801"))(100)) +
ggtitle("B")
# Create Venn diagram
venn_plot <- ggplot() +
ggforce::geom_circle(data = data.frame(x = c(-1, 1), y = c(0, 0), r = c(2, 2),
category = c("RIN Features", "Autolysis Features")),
aes(x0 = x, y0 = y, r = r, fill = category),
alpha = 0.5, color = "black", size = 0.8) +
geom_text(data = data.frame(x = c(-1.65, 0, 1.65), y = c(0, 0, 0),
label = c(length(rin_only_features),
length(common_features),
length(auto_only_features))),
aes(x = x, y = y, label = label), size = 10, fontface = "bold") +
scale_fill_manual(values = c("RIN Features" = "#66CDAA", "Autolysis Features" = "#FC8D62")) +
theme_void() +
theme(legend.position = "none") +
coord_fixed() +
xlim(-3, 3) + ylim(-2, 2) +
ggtitle("C")
# Combine plots
heatmaps <- plot_grid(rin_heatmap, auto_heatmap, ncol = 2)
figure4 <- plot_grid(heatmaps, venn_plot, nrow = 2, rel_heights = c(2, 1.5))
ggsave(file.path(output_path, "Figure4_Feature.pdf"),
figure4, width = 14, height = 12, dpi = 600, bg = "transparent")
#—————————————————————————— # 7. Additional Visualizations and
Comparative Analysis #——————————————————————————
# Create comparative performance chart for top tissues
create_comparative_bar_chart <- function(rin_results, autolysis_results) {
common_tissues <- intersect(rin_results$Tissue, autolysis_results$Tissue)
comparison_data <- data.frame(
Tissue = common_tissues,
RIN_R = rin_results$R[match(common_tissues, rin_results$Tissue)],
Autolysis_R = autolysis_results$R[match(common_tissues, autolysis_results$Tissue)]
)
comparison_data$Average_R <- (abs(comparison_data$RIN_R) + abs(comparison_data$Autolysis_R)) / 2
comparison_data <- comparison_data[order(-comparison_data$Average_R), ]
top_tissues <- head(comparison_data, 15)
plot_data <- reshape2::melt(top_tissues[, c("Tissue", "RIN_R", "Autolysis_R")],
id.vars = "Tissue", variable.name = "Model", value.name = "R_value")
plot_data$Tissue <- factor(plot_data$Tissue, levels = top_tissues$Tissue)
comparison_plot <- ggplot(plot_data, aes(x = Tissue, y = R_value, fill = Model)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8) +
geom_text(aes(label = sprintf("%.3f", R_value)),
position = position_dodge(width = 0.9), vjust = -0.3, size = 3) +
scale_fill_manual(values = c("RIN_R" = "#66CDAA", "Autolysis_R" = "#FC8D62"),
labels = c("RIN", "Autolysis")) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
plot.title = element_text(size = 16, face = "bold"),
legend.position = "top"
) +
labs(
title = "Model Performance Comparison Across Top Tissues",
subtitle = "Top 15 tissues ranked by average correlation coefficient",
x = "Tissue Type",
y = "Correlation Coefficient (R)",
fill = "Model Type"
)
ggsave(file.path(output_path, "comparative_performance_chart.pdf"),
comparison_plot, width = 14, height = 8, dpi = 300)
return(comparison_plot)
}
# Create correlation distribution analysis
create_correlation_distribution <- function(results_df, target_name) {
r_dist_plot <- ggplot(results_df, aes(x = abs(R))) +
geom_histogram(binwidth = 0.05, fill = "steelblue", color = "white", alpha = 0.8) +
geom_vline(aes(xintercept = mean(abs(R))),
color = "red", linetype = "dashed", size = 1.2) +
geom_vline(aes(xintercept = median(abs(R))),
color = "darkgreen", linetype = "dashed", size = 1.2) +
annotate("text", x = mean(abs(results_df$R)) + 0.08, y = max(table(cut(abs(results_df$R), 20))) * 0.8,
label = paste("Mean =", round(mean(abs(results_df$R)), 3)),
color = "red", fontface = "bold") +
annotate("text", x = median(abs(results_df$R)) - 0.08, y = max(table(cut(abs(results_df$R), 20))) * 0.6,
label = paste("Median =", round(median(abs(results_df$R)), 3)),
color = "darkgreen", fontface = "bold") +
theme_minimal() +
labs(
title = paste("Distribution of Correlation Coefficients:", target_name),
subtitle = paste("Analysis of", nrow(results_df), "tissue-specific models"),
x = "Absolute Correlation Coefficient |R|",
y = "Frequency"
) +
theme(
plot.title = element_text(size = 14, face = "bold"),
axis.title = element_text(size = 12, face = "bold")
)
ggsave(file.path(output_path, paste0(gsub(" ", "_", tolower(target_name)), "_correlation_distribution.pdf")),
r_dist_plot, width = 10, height = 7, dpi = 300)
return(r_dist_plot)
}
# Create enhanced scatter plots with residual analysis
create_enhanced_scatter_plots <- function(results, target_name, top_n = 6) {
all_r_values <- sapply(results, function(x) {
if(is.null(x)) return(0)
return(abs(x$performance$pooled_r))
})
top_tissues <- names(sort(all_r_values, decreasing = TRUE))[1:min(top_n, length(all_r_values))]
for(tissue in top_tissues) {
if(is.null(results[[tissue]])) next
predictions <- results[[tissue]]$all_predictions
r_value <- results[[tissue]]$performance$pooled_r
rmse_value <- results[[tissue]]$performance$pooled_rmse
predictions$residual <- predictions$predicted - predictions$actual
scatter_plot <- ggplot(predictions, aes(x = actual, y = predicted)) +
geom_point(alpha = 0.7, color = "#2E86AB", size = 2.5) +
geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed", size = 1) +
geom_smooth(method = "lm", se = TRUE, color = "darkblue", alpha = 0.3) +
labs(
title = paste(tissue, "-", target_name, "Prediction Model"),
subtitle = paste("R =", round(r_value, 3), "| RMSE =", round(rmse_value, 3)),
x = paste("Actual", target_name),
y = paste("Predicted", target_name)
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 11, face = "bold")
)
ggsave(file.path(output_path, paste0("enhanced_", tissue, "_", gsub(" ", "_", tolower(target_name)), "_scatter.pdf")),
scatter_plot, width = 10, height = 8, dpi = 300)
}
}
# Execute comparative visualizations
comparison_chart <- create_comparative_bar_chart(rin_summary, autolysis_summary)
rin_distribution <- create_correlation_distribution(rin_summary, "RIN Score")
autolysis_distribution <- create_correlation_distribution(autolysis_summary, "Autolysis Score")
create_enhanced_scatter_plots(all_results[["SMRIN"]], "RIN Score", top_n = 6)
create_enhanced_scatter_plots(all_results[["SMATSSCR"]], "Autolysis Score", top_n = 6)
#—————————————————————————— # 8. Figure 5: Model Performance vs
Sample Size Analysis #——————————————————————————
create_sample_size_analysis <- function(rin_summary, autolysis_summary) {
combined_data <- rbind(
data.frame(
Tissue = rin_summary$Tissue,
Samples = rin_summary$Samples,
R_value = abs(rin_summary$R),
Model = "RIN"
),
data.frame(
Tissue = autolysis_summary$Tissue,
Samples = autolysis_summary$Samples,
R_value = abs(autolysis_summary$R),
Model = "Autolysis"
)
)
scatter_panel <- ggplot(combined_data, aes(x = Samples, y = R_value, color = Model)) +
geom_point(size = 3.5, alpha = 0.8) +
geom_smooth(method = "lm", se = TRUE, alpha = 0.3, size = 1.2) +
scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "A",
x = "Sample Size (n)",
y = "Correlation Coefficient |R|",
subtitle = "Relationship between sample size and model performance"
) +
theme(
plot.title = element_text(size = 20, face = "bold", hjust = 0),
plot.subtitle = element_text(size = 12),
legend.position = "top",
legend.title = element_text(size = 12, face = "bold"),
axis.title = element_text(size = 12, face = "bold")
)
combined_data$size_category <- cut(combined_data$Samples,
breaks = c(0, 50, 100, 200, 500, Inf),
labels = c("≤50", "51-100", "101-200", "201-500", ">500"))
bin_summary <- combined_data %>%
group_by(size_category, Model) %>%
summarise(
mean_r = mean(R_value, na.rm = TRUE),
se_r = sd(R_value, na.rm = TRUE) / sqrt(n()),
tissue_count = n(),
.groups = 'drop'
)
bin_panel <- ggplot(bin_summary, aes(x = size_category, y = mean_r, fill = Model)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.85, width = 0.7) +
geom_errorbar(aes(ymin = mean_r - se_r, ymax = mean_r + se_r),
position = position_dodge(width = 0.7), width = 0.25, size = 0.8) +
geom_text(aes(label = tissue_count),
position = position_dodge(width = 0.7),
vjust = -1.5, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "B",
x = "Sample Size Categories",
y = "Mean |R| ± SE",
subtitle = "Performance stratified by sample size (numbers show tissue count)"
) +
theme(
plot.title = element_text(size = 20, face = "bold", hjust = 0),
plot.subtitle = element_text(size = 12),
legend.position = "none",
axis.title = element_text(size = 12, face = "bold")
)
figure5_combined <- grid.arrange(scatter_panel, bin_panel, ncol = 2, widths = c(1, 1))
return(figure5_combined)
}
analyze_sample_size_effects <- function(combined_data) {
rin_data <- combined_data[combined_data$Model == "RIN", ]
auto_data <- combined_data[combined_data$Model == "Autolysis", ]
rin_cor <- cor.test(rin_data$Samples, rin_data$R_value)
auto_cor <- cor.test(auto_data$Samples, auto_data$R_value)
return(list(rin_test = rin_cor, autolysis_test = auto_cor))
}
figure5_plot <- create_sample_size_analysis(rin_summary, autolysis_summary)

sample_size_stats <- analyze_sample_size_effects(rbind(
data.frame(Tissue = rin_summary$Tissue, Samples = rin_summary$Samples,
R_value = abs(rin_summary$R), Model = "RIN"),
data.frame(Tissue = autolysis_summary$Tissue, Samples = autolysis_summary$Samples,
R_value = abs(autolysis_summary$R), Model = "Autolysis")
))
ggsave(file.path(output_path, "Figure5_Sample_Size_Analysis.pdf"),
figure5_plot, width = 16, height = 8, dpi = 600, bg = "white")
G2;H2;Warningh in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
for '≤50' in 'mbcsToSbcs': <= substituted for ≤ (U+2264)g
#—————————————————————————— # 9. Top 5 Tissues Quality Overlay
#——————————————————————————
# Get top 5 tissues for each score
get_top5_tissues <- function(target_name) {
r_values <- sapply(all_results[[target_name]], function(x) {
if(is.null(x) || is.null(x$performance$pooled_r2)) return(0)
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
return(abs(x$performance$pooled_r)) # Use absolute R for ranking
})
r_values <- r_values[r_values > 0]
top5 <- sort(r_values, decreasing = TRUE)[1:5]
return(data.frame(
tissue = names(top5),
correlation_r = as.numeric(top5)
))
}
top5_rin <- get_top5_tissues("SMRIN")
top5_auto <- get_top5_tissues("SMATSSCR")
# Create overlay plots
create_simple_overlay <- function(tissue_name, score_type, color_palette) {
tissue_data <- merged_gtex[tissue_main == tissue_name]
if(nrow(tissue_data) < 10) {
return(ggplot() + theme_void())
}
# Get features and run UMAP
feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
feature_matrix <- as.matrix(tissue_data[, ..feature_cols])
feature_matrix <- scale(feature_matrix[, apply(feature_matrix, 2, var) > 1e-10])
set.seed(123)
umap_result <- umap(feature_matrix, n_neighbors = min(15, nrow(feature_matrix) - 1))
plot_data <- data.frame(
UMAP1 = umap_result[, 1],
UMAP2 = umap_result[, 2],
Score = tissue_data[[score_type]]
)
# Separate data with and without scores
data_with_scores <- plot_data[!is.na(plot_data$Score), ]
data_missing_scores <- plot_data[is.na(plot_data$Score), ]
# Get R value
tissue_result <- all_results[[score_type]][[tissue_name]]
r_value <- if(!is.null(tissue_result)) tissue_result$performance$pooled_r else 0
# r_value <- if(!is.null(tissue_result)) sqrt(tissue_result$performance$pooled_r2) else 0 # use when required for R²
p <- ggplot() +
geom_point(data = data_missing_scores, aes(x = UMAP1, y = UMAP2),
color = "lightgrey", alpha = 0.5, size = 1.5) +
geom_point(data = data_with_scores, aes(x = UMAP1, y = UMAP2, color = Score),
alpha = 0.7, size = 2) +
color_palette +
theme_minimal() +
labs(
title = tissue_name,
subtitle = paste("R =", sprintf("%.3f", r_value)),
x = "UMAP1", y = "UMAP2"
)
return(p)
}
# Define color palettes
rin_palette <- scale_color_viridis_c(name = "RIN\nScore", option = "viridis")
auto_palette <- scale_color_viridis_c(name = "Autolysis\nScore", option = "plasma")
# Create plots
rin_plots <- lapply(1:5, function(i) {
create_simple_overlay(top5_rin$tissue[i], "SMRIN", rin_palette)
})
auto_plots <- lapply(1:5, function(i) {
create_simple_overlay(top5_auto$tissue[i], "SMATSSCR", auto_palette)
})
# Combine panels
panel_a <- grid.arrange(grobs = rin_plots, nrow = 1,
top = textGrob("A", gp = gpar(fontsize = 20, fontface = "bold"),
x = 0.02, just = "left"))

panel_b <- grid.arrange(grobs = auto_plots, nrow = 1,
top = textGrob("B", gp = gpar(fontsize = 20, fontface = "bold"),
x = 0.02, just = "left"))

final_plot <- grid.arrange(panel_a, panel_b, nrow = 2)

ggsave(file.path(output_path, "Top5_Tissues_Quality_Overlay_AB.pdf"),
final_plot, width = 20, height = 12, dpi = 300, bg = "white")
#—————————————————————————— # 10. Figure 6: Cross-Tissue Feature
Consistency Analysis #——————————————————————————
create_feature_consistency_analysis <- function(all_results) {
extract_consistent_features <- function(target_results, min_selections = 3) {
tissue_features <- list()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
features <- names(target_results[[tissue]]$feature_counts)
counts <- as.numeric(target_results[[tissue]]$feature_counts)
selected_features <- features[counts >= min_selections]
tissue_features[[tissue]] <- selected_features
}
}
return(tissue_features)
}
rin_tissue_features <- extract_consistent_features(all_results[["SMRIN"]])
auto_tissue_features <- extract_consistent_features(all_results[["SMATSSCR"]])
create_consistency_heatmap <- function(tissue_features, title, color_scheme) {
all_features <- unique(unlist(tissue_features))
all_tissues <- names(tissue_features)
selection_matrix <- matrix(0, nrow = length(all_features), ncol = length(all_tissues))
rownames(selection_matrix) <- all_features
colnames(selection_matrix) <- all_tissues
for(tissue in all_tissues) {
selected <- tissue_features[[tissue]]
selection_matrix[selected, tissue] <- 1
}
consistency_scores <- rowSums(selection_matrix) / ncol(selection_matrix)
top_features <- names(sort(consistency_scores, decreasing = TRUE))[1:25]
plot_matrix <- selection_matrix[top_features, ]
plot_data <- expand.grid(Feature = rownames(plot_matrix),
Tissue = colnames(plot_matrix))
plot_data$Selected <- as.vector(plot_matrix)
plot_data$Consistency_Score <- consistency_scores[plot_data$Feature]
plot_data$Feature_ID <- sub("feature_", "", plot_data$Feature)
heatmap_plot <- ggplot(plot_data, aes(x = Tissue, y = reorder(Feature_ID, Consistency_Score))) +
geom_tile(aes(fill = factor(Selected)), color = "white", size = 0.3) +
scale_fill_manual(values = c("0" = "white", "1" = color_scheme),
name = "Selected", labels = c("No", "Yes")) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 9, face = "bold"),
axis.text.y = element_text(size = 9),
panel.grid = element_blank(),
legend.position = "right",
plot.title = element_text(size = 16, face = "bold")
) +
labs(
title = title,
x = NULL,
y = "Feature ID"
)
return(heatmap_plot)
}
panel_a <- create_consistency_heatmap(rin_tissue_features, "A. RIN Model Feature Consistency", "#2E8B57")
panel_b <- create_consistency_heatmap(auto_tissue_features, "B. Autolysis Model Feature Consistency", "#D2691E")
calculate_stability_distribution <- function(tissue_features) {
all_features <- unique(unlist(tissue_features))
stability_scores <- sapply(all_features, function(feature) {
tissues_with_feature <- sum(sapply(tissue_features, function(x) feature %in% x))
return(tissues_with_feature / length(tissue_features))
})
return(stability_scores)
}
rin_stability <- calculate_stability_distribution(rin_tissue_features)
auto_stability <- calculate_stability_distribution(auto_tissue_features)
stability_comparison <- data.frame(
Feature = c(names(rin_stability), names(auto_stability)),
Stability = c(rin_stability, auto_stability),
Model = rep(c("RIN", "Autolysis"), c(length(rin_stability), length(auto_stability)))
)
panel_c <- ggplot(stability_comparison, aes(x = Stability, fill = Model)) +
geom_histogram(alpha = 0.75, bins = 15, position = "identity") +
geom_vline(data = stability_comparison %>% group_by(Model) %>% summarise(mean_stab = mean(Stability)),
aes(xintercept = mean_stab, color = Model), linetype = "dashed", size = 1.2) +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
scale_color_manual(values = c("RIN" = "#2E8B57", "Autolysis" = "#D2691E")) +
theme_minimal() +
labs(
title = "C. Feature Stability Distribution",
x = "Stability Score (Fraction of Tissues)",
y = "Number of Features",
subtitle = "Distribution of cross-tissue feature consistency"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 11, face = "bold")
)
top_panels <- grid.arrange(panel_a, panel_b, ncol = 2)
figure6_combined <- grid.arrange(top_panels, panel_c, nrow = 2, heights = c(2, 1))
return(figure6_combined)
}
calculate_feature_overlap_stats <- function(all_results) {
extract_top_features <- function(target_results, n = 50) {
feature_counts <- list()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
counts <- target_results[[tissue]]$feature_counts
for(feat in names(counts)) {
if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
}
}
}
sorted_features <- sort(unlist(feature_counts), decreasing = TRUE)
return(names(sorted_features)[1:min(n, length(sorted_features))])
}
rin_top <- extract_top_features(all_results[["SMRIN"]])
auto_top <- extract_top_features(all_results[["SMATSSCR"]])
overlap_count <- length(intersect(rin_top, auto_top))
rin_unique <- length(setdiff(rin_top, auto_top))
auto_unique <- length(setdiff(auto_top, rin_top))
return(list(common = overlap_count, rin_unique = rin_unique, auto_unique = auto_unique))
}
figure6_plot <- create_feature_consistency_analysis(all_results)


overlap_stats <- calculate_feature_overlap_stats(all_results)
ggsave(file.path(output_path, "Figure6_Feature_Consistency.pdf"),
figure6_plot, width = 18, height = 14, dpi = 600, bg = "white")
#—————————————————————————— # 11. Figure 7: Model Residual Analysis
#——————————————————————————
create_comprehensive_residual_analysis <- function(all_results, top_n = 6) {
identify_top_tissues <- function(target_results, n) {
performance_scores <- sapply(target_results, function(x) {
if(is.null(x)) return(0)
return(abs(x$performance$pooled_r))
})
top_tissues <- names(sort(performance_scores, decreasing = TRUE))[1:n]
return(top_tissues[!is.na(top_tissues)])
}
top_rin_tissues <- identify_top_tissues(all_results[["SMRIN"]], top_n)
top_auto_tissues <- identify_top_tissues(all_results[["SMATSSCR"]], top_n)
create_residual_panel <- function(tissues, target, color, panel_title) {
residual_plots <- list()
for(i in 1:min(3, length(tissues))) {
tissue_name <- tissues[i]
result <- all_results[[target]][[tissue_name]]
if(!is.null(result) && !is.null(result$all_predictions)) {
predictions <- result$all_predictions
predictions$residual <- predictions$predicted - predictions$actual
r_value <- result$performance$pooled_r
residual_plot <- ggplot(predictions, aes(x = actual, y = residual)) +
geom_point(alpha = 0.7, color = color, size = 2) +
geom_hline(yintercept = 0, linetype = "dashed", color = "red", size = 1) +
geom_smooth(method = "loess", se = TRUE, color = "darkblue", alpha = 0.3) +
theme_minimal() +
labs(
title = paste(tissue_name, "(R =", round(r_value, 3), ")"),
x = if(i == 3) paste("Actual", ifelse(target == "SMRIN", "RIN", "Autolysis")) else "",
y = if(i == 1) "Residual" else ""
) +
theme(
plot.title = element_text(size = 12, face = "bold"),
axis.title = element_text(size = 10, face = "bold")
)
residual_plots[[i]] <- residual_plot
}
}
panel <- grid.arrange(grobs = residual_plots, nrow = 1,
top = textGrob(panel_title, gp = gpar(fontsize = 16, fontface = "bold")))
return(panel)
}
panel_a <- create_residual_panel(top_rin_tissues, "SMRIN", "#66CDAA", "A. RIN Model Residuals")
panel_b <- create_residual_panel(top_auto_tissues, "SMATSSCR", "#FC8D62", "B. Autolysis Model Residuals")
create_residual_distribution <- function() {
all_residuals <- data.frame()
for(target in c("SMRIN", "SMATSSCR")) {
for(tissue in names(all_results[[target]])) {
result <- all_results[[target]][[tissue]]
if(!is.null(result) && !is.null(result$all_predictions)) {
predictions <- result$all_predictions
residual_data <- data.frame(
tissue = tissue,
target = ifelse(target == "SMRIN", "RIN", "Autolysis"),
residual = predictions$predicted - predictions$actual,
abs_residual = abs(predictions$predicted - predictions$actual)
)
all_residuals <- rbind(all_residuals, residual_data)
}
}
}
distribution_plot <- ggplot(all_residuals, aes(x = abs_residual, fill = target)) +
geom_histogram(alpha = 0.75, bins = 30, position = "identity") +
geom_vline(data = all_residuals %>% group_by(target) %>% summarise(mean_res = mean(abs_residual)),
aes(xintercept = mean_res, color = target), linetype = "dashed", size = 1.2) +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
scale_color_manual(values = c("RIN" = "#2E8B57", "Autolysis" = "#D2691E")) +
theme_minimal() +
labs(
title = "C. Distribution of Absolute Residuals",
x = "Absolute Residual Magnitude",
y = "Frequency",
fill = "Model Type",
subtitle = "Comparison of prediction errors across all tissue models"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 11, face = "bold")
)
return(distribution_plot)
}
create_performance_residual_relationship <- function() {
performance_residual_data <- data.frame()
for(target in c("SMRIN", "SMATSSCR")) {
for(tissue in names(all_results[[target]])) {
result <- all_results[[target]][[tissue]]
if(!is.null(result) && !is.null(result$all_predictions)) {
predictions <- result$all_predictions
perf_data <- data.frame(
tissue = tissue,
target = ifelse(target == "SMRIN", "RIN", "Autolysis"),
r_value = abs(result$performance$pooled_r),
rmse = result$performance$pooled_rmse,
mean_abs_residual = mean(abs(predictions$predicted - predictions$actual))
)
performance_residual_data <- rbind(performance_residual_data, perf_data)
}
}
}
relationship_plot <- ggplot(performance_residual_data, aes(x = r_value, y = mean_abs_residual, color = target)) +
geom_point(size = 3.5, alpha = 0.8) +
geom_smooth(method = "lm", se = TRUE, alpha = 0.3, size = 1.2) +
scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "D. Model Performance vs Residual Magnitude",
x = "Correlation Coefficient |R|",
y = "Mean Absolute Residual",
color = "Model Type",
subtitle = "Relationship between prediction accuracy and error magnitude"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "none",
axis.title = element_text(size = 11, face = "bold")
)
return(relationship_plot)
}
panel_c <- create_residual_distribution()
panel_d <- create_performance_residual_relationship()
bottom_panels <- grid.arrange(panel_c, panel_d, ncol = 2)
figure7_combined <- grid.arrange(panel_a, panel_b, bottom_panels, nrow = 3, heights = c(1, 1, 1))
return(figure7_combined)
}
analyze_residual_patterns <- function(all_results) {
residual_stats <- data.frame()
for(target in c("SMRIN", "SMATSSCR")) {
for(tissue in names(all_results[[target]])) {
result <- all_results[[target]][[tissue]]
if(!is.null(result) && !is.null(result$all_predictions)) {
predictions <- result$all_predictions
residuals <- predictions$predicted - predictions$actual
stats_row <- data.frame(
tissue = tissue,
target = target,
mean_residual = mean(residuals),
sd_residual = sd(residuals),
skewness = e1071::skewness(residuals),
normality_p = shapiro.test(sample(residuals, min(5000, length(residuals))))$p.value
)
residual_stats <- rbind(residual_stats, stats_row)
}
}
}
return(residual_stats)
}
figure7_plot <- create_comprehensive_residual_analysis(all_results)
residual_statistics <- analyze_residual_patterns(all_results)
ggsave(file.path(output_path, "Figure7_Residual_Analysis.pdf"),
figure7_plot, width = 16, height = 14, dpi = 600, bg = "white")
#—————————————————————————— # 11. Figure 8: Biological Feature
Interpretation #——————————————————————————
create_biological_interpretation_analysis <- function(all_results, merged_gtex) {
analyze_feature_categories <- function() {
extract_important_features <- function(target_results, n = 50) {
feature_importance <- c()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
features <- names(target_results[[tissue]]$feature_counts)
counts <- as.numeric(target_results[[tissue]]$feature_counts)
feature_importance <- c(feature_importance, rep(features, counts))
}
}
importance_table <- sort(table(feature_importance), decreasing = TRUE)
return(names(importance_table)[1:min(n, length(importance_table))])
}
rin_important <- extract_important_features(all_results[["SMRIN"]])
auto_important <- extract_important_features(all_results[["SMATSSCR"]])
categorize_features <- function(feature_names) {
feature_numbers <- as.numeric(sub("feature_", "", feature_names))
categories <- case_when(
feature_numbers <= 1024 ~ "Mean",
feature_numbers <= 2048 ~ "Standard Deviation",
feature_numbers <= 3072 ~ "Minimum",
TRUE ~ "Maximum"
)
return(categories)
}
rin_categories <- categorize_features(rin_important)
auto_categories <- categorize_features(auto_important)
category_summary <- data.frame(
Category = rep(c("Mean", "Standard Deviation", "Minimum", "Maximum"), 2),
Count = c(table(rin_categories)[c("Mean", "Standard Deviation", "Minimum", "Maximum")],
table(auto_categories)[c("Mean", "Standard Deviation", "Minimum", "Maximum")]),
Model = rep(c("RIN", "Autolysis"), each = 4)
)
category_summary$Count[is.na(category_summary$Count)] <- 0
category_plot <- ggplot(category_summary, aes(x = Category, y = Count, fill = Model)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.85, width = 0.7) +
geom_text(aes(label = Count), position = position_dodge(width = 0.7),
vjust = -0.3, size = 4, fontface = "bold") +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "A. Feature Category Distribution",
x = "Statistical Property Category",
y = "Number of Important Features",
subtitle = "Distribution of key features across statistical measures"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 12, face = "bold"),
axis.text.x = element_text(size = 11, face = "bold")
)
return(category_plot)
}
create_tissue_variability_analysis <- function() {
feature_cols <- grep("^feature_", names(merged_gtex), value = TRUE)
get_feature_importance <- function(target_results) {
feature_scores <- list()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
counts <- target_results[[tissue]]$feature_counts
for(feat in names(counts)) {
if(is.null(feature_scores[[feat]])) feature_scores[[feat]] <- 0
feature_scores[[feat]] <- feature_scores[[feat]] + counts[[feat]]
}
}
}
sorted_scores <- sort(unlist(feature_scores), decreasing = TRUE)
return(names(sorted_scores)[1:min(25, length(sorted_scores))])
}
important_features <- get_feature_importance(all_results[["SMRIN"]])
variability_data <- data.frame()
for(feature in important_features) {
if(feature %in% feature_cols) {
tissue_statistics <- merged_gtex[, .(
mean_value = mean(get(feature), na.rm = TRUE),
sd_value = sd(get(feature), na.rm = TRUE)
), by = tissue_main]
overall_variance <- var(tissue_statistics$mean_value, na.rm = TRUE)
importance_score <- sum(sapply(all_results[["SMRIN"]], function(x) {
if(!is.null(x$feature_counts) && feature %in% names(x$feature_counts)) {
return(x$feature_counts[[feature]])
}
return(0)
}))
feature_num <- as.numeric(sub("feature_", "", feature))
category <- case_when(
feature_num <= 1024 ~ "Mean",
feature_num <= 2048 ~ "Std Dev",
feature_num <= 3072 ~ "Minimum",
TRUE ~ "Maximum"
)
variability_data <- rbind(variability_data, data.frame(
feature = feature,
tissue_variance = overall_variance,
importance_score = importance_score,
category = category
))
}
}
variability_plot <- ggplot(variability_data, aes(x = tissue_variance, y = importance_score, color = category)) +
geom_point(size = 4, alpha = 0.8) +
geom_smooth(method = "lm", se = TRUE, alpha = 0.3, color = "darkblue") +
scale_color_brewer(palette = "Set2", name = "Feature\nCategory") +
theme_minimal() +
labs(
title = "B. Feature Importance vs Tissue Variability",
x = "Variance Across Tissue Types",
y = "Importance Score (Selection Frequency)",
subtitle = "Relationship between tissue discrimination and feature importance"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "right",
axis.title = element_text(size = 12, face = "bold")
)
return(variability_plot)
}
create_feature_correlation_matrix <- function() {
extract_consensus_features <- function(target_results, n = 20) {
feature_counts <- list()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
counts <- target_results[[tissue]]$feature_counts
for(feat in names(counts)) {
if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
}
}
}
sorted_features <- sort(unlist(feature_counts), decreasing = TRUE)
return(names(sorted_features)[1:min(n, length(sorted_features))])
}
consensus_features <- extract_consensus_features(all_results[["SMRIN"]], 15)
if(length(consensus_features) > 1) {
feature_matrix <- as.matrix(merged_gtex[, ..consensus_features])
feature_matrix <- feature_matrix[complete.cases(feature_matrix), ]
if(nrow(feature_matrix) > 10) {
correlation_matrix <- cor(feature_matrix, use = "complete.obs")
cor_long <- reshape2::melt(correlation_matrix)
cor_long$Var1 <- sub("feature_", "", cor_long$Var1)
cor_long$Var2 <- sub("feature_", "", cor_long$Var2)
correlation_plot <- ggplot(cor_long, aes(x = Var1, y = Var2, fill = value)) +
geom_tile(color = "white", size = 0.5) +
scale_fill_gradient2(low = "#2166AC", mid = "white", high = "#D73027",
midpoint = 0, name = "Correlation\nCoefficient",
limits = c(-1, 1)) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
axis.text.y = element_text(size = 10),
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "right"
) +
labs(
title = "C. Feature Correlation Matrix",
x = "Feature ID",
y = "Feature ID",
subtitle = "Correlation structure among top predictive features"
) +
coord_fixed()
return(correlation_plot)
}
}
empty_plot <- ggplot() +
annotate("text", x = 0.5, y = 0.5,
label = "Insufficient data\nfor correlation analysis",
size = 6, hjust = 0.5) +
theme_void() +
labs(title = "C. Feature Correlation Matrix") +
theme(plot.title = element_text(size = 16, face = "bold", hjust = 0.5))
return(empty_plot)
}
panel_a <- analyze_feature_categories()
panel_b <- create_tissue_variability_analysis()
panel_c <- create_feature_correlation_matrix()
top_panels <- grid.arrange(panel_a, panel_b, ncol = 2)
figure8_combined <- grid.arrange(top_panels, panel_c, nrow = 2, heights = c(1, 1))
return(figure8_combined)
}
analyze_biological_significance <- function(all_results) {
for(target in c("SMRIN", "SMATSSCR")) {
all_features <- unlist(lapply(all_results[[target]], function(x) {
if(!is.null(x$feature_counts)) names(x$feature_counts) else NULL
}))
feature_nums <- as.numeric(sub("feature_", "", all_features))
mean_features <- sum(feature_nums <= 1024)
std_features <- sum(feature_nums > 1024 & feature_nums <= 2048)
min_features <- sum(feature_nums > 2048 & feature_nums <= 3072)
max_features <- sum(feature_nums > 3072)
}
}
figure8_plot <- create_biological_interpretation_analysis(all_results, merged_gtex)


analyze_biological_significance(all_results)
ggsave(file.path(output_path, "Figure8_Biological_Interpretation.pdf"),
figure8_plot, width = 16, height = 14, dpi = 600, bg = "white")
#—————————————————————————— # 12. Supplementary Figure S1: Quality
Score Distributions by Tissue #——————————————————————————
create_quality_score_distributions <- function(merged_gtex) {
prepare_quality_data <- function() {
rin_data <- merged_gtex[!is.na(SMRIN), .(tissue_main, SMRIN)]
setnames(rin_data, "SMRIN", "Score")
rin_data$Score_Type <- "RIN Score"
auto_data <- merged_gtex[!is.na(SMATSSCR), .(tissue_main, SMATSSCR)]
setnames(auto_data, "SMATSSCR", "Score")
auto_data$Score_Type <- "Autolysis Score"
return(list(rin = rin_data, autolysis = auto_data))
}
get_top_tissues <- function(n_tissues = 20) {
tissue_counts <- merged_gtex[, .N, by = tissue_main][order(-N)]
return(head(tissue_counts$tissue_main, n_tissues))
}
quality_data <- prepare_quality_data()
top_tissues <- get_top_tissues()
rin_filtered <- quality_data$rin[tissue_main %in% top_tissues]
auto_filtered <- quality_data$autolysis[tissue_main %in% top_tissues]
rin_filtered$tissue_main <- factor(rin_filtered$tissue_main, levels = top_tissues)
auto_filtered$tissue_main <- factor(auto_filtered$tissue_main, levels = top_tissues)
create_rin_distribution_panel <- function() {
rin_violin <- ggplot(rin_filtered, aes(x = tissue_main, y = Score)) +
geom_violin(fill = "#66CDAA", alpha = 0.8, trim = FALSE, scale = "width") +
geom_boxplot(width = 0.15, alpha = 0.9, outlier.size = 1, outlier.alpha = 0.6) +
stat_summary(fun = median, geom = "point", color = "darkblue", size = 1.5) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
plot.title = element_text(size = 18, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12, face = "bold")
) +
labs(
title = "A. RIN Score Distributions by Tissue Type",
subtitle = "RNA Integrity Number across major tissue types",
x = NULL,
y = "RIN Score"
) +
scale_y_continuous(limits = c(1, 10), breaks = seq(2, 10, 2))
return(rin_violin)
}
create_autolysis_distribution_panel <- function() {
auto_violin <- ggplot(auto_filtered, aes(x = tissue_main, y = Score)) +
geom_violin(fill = "#FC8D62", alpha = 0.8, trim = FALSE, scale = "width") +
geom_boxplot(width = 0.15, alpha = 0.9, outlier.size = 1, outlier.alpha = 0.6) +
stat_summary(fun = median, geom = "point", color = "darkred", size = 1.5) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
plot.title = element_text(size = 18, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12, face = "bold")
) +
labs(
title = "B. Autolysis Score Distributions by Tissue Type",
subtitle = "Tissue degradation levels across major tissue types",
x = "Tissue Type",
y = "Autolysis Score"
) +
scale_y_continuous(limits = c(0, 3), breaks = 0:3,
labels = c("0 (None)", "1 (Mild)", "2 (Moderate)", "3 (Severe)"))
return(auto_violin)
}
calculate_distribution_stats <- function() {
rin_stats <- rin_filtered[, .(
mean_score = round(mean(Score, na.rm = TRUE), 2),
median_score = round(median(Score, na.rm = TRUE), 2),
sd_score = round(sd(Score, na.rm = TRUE), 2),
sample_size = .N
), by = tissue_main][order(-mean_score)]
auto_stats <- auto_filtered[, .(
mean_score = round(mean(Score, na.rm = TRUE), 2),
median_score = round(median(Score, na.rm = TRUE), 2),
sd_score = round(sd(Score, na.rm = TRUE), 2),
sample_size = .N
), by = tissue_main][order(mean_score)]
return(list(rin_stats = rin_stats, auto_stats = auto_stats))
}
panel_a <- create_rin_distribution_panel()
panel_b <- create_autolysis_distribution_panel()
distribution_stats <- calculate_distribution_stats()
supp_fig_s1 <- grid.arrange(panel_a, panel_b, nrow = 2)
return(supp_fig_s1)
}
analyze_quality_correlations <- function(merged_gtex) {
quality_subset <- merged_gtex[!is.na(SMRIN) & !is.na(SMATSSCR)]
if(nrow(quality_subset) > 10) {
correlation_test <- cor.test(quality_subset$SMRIN, quality_subset$SMATSSCR)
return(correlation_test)
} else {
return(NULL)
}
}
supp_figure_s1 <- create_quality_score_distributions(merged_gtex)

quality_correlations <- analyze_quality_correlations(merged_gtex)
ggsave(file.path(output_path, "SuppFig_S1_Quality_Distributions.pdf"),
supp_figure_s1, width = 18, height = 12, dpi = 600, bg = "white")
#—————————————————————————— # 13. Supplementary Figure S2:
Cross-validation Stability Analysis #——————————————————————————
create_cv_stability_analysis <- function(all_results) {
extract_fold_performance <- function(target_results) {
fold_performance <- data.frame()
for(tissue in names(target_results)) {
result <- target_results[[tissue]]
if(!is.null(result) && !is.null(result$fold_results)) {
for(fold_result in result$fold_results) {
fold_data <- data.frame(
tissue = tissue,
fold = fold_result$fold,
r_value = abs(fold_result$r),
rmse = fold_result$rmse,
mae = fold_result$mae
)
fold_performance <- rbind(fold_performance, fold_data)
}
}
}
return(fold_performance)
}
rin_fold_data <- extract_fold_performance(all_results[["SMRIN"]])
auto_fold_data <- extract_fold_performance(all_results[["SMATSSCR"]])
rin_fold_data$target <- "RIN"
auto_fold_data$target <- "Autolysis"
combined_fold_data <- rbind(rin_fold_data, auto_fold_data)
create_stability_scatter <- function() {
stability_metrics <- combined_fold_data %>%
group_by(tissue, target) %>%
summarise(
mean_r = mean(r_value, na.rm = TRUE),
sd_r = sd(r_value, na.rm = TRUE),
cv_r = sd(r_value, na.rm = TRUE) / mean(r_value, na.rm = TRUE),
.groups = 'drop'
) %>%
filter(is.finite(cv_r) & cv_r > 0)
stability_plot <- ggplot(stability_metrics, aes(x = mean_r, y = cv_r, color = target)) +
geom_point(size = 3.5, alpha = 0.8) +
geom_smooth(method = "lm", se = TRUE, alpha = 0.3) +
scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "A. Model Stability vs Performance",
x = "Mean |R| Across Folds",
y = "Coefficient of Variation (|R|)",
color = "Model Type",
subtitle = "Lower CV indicates more stable cross-validation performance"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 12, face = "bold")
)
return(stability_plot)
}
create_fold_variability_boxplots <- function() {
top_tissues_rin <- combined_fold_data %>%
filter(target == "RIN") %>%
group_by(tissue) %>%
summarise(mean_r = mean(r_value, na.rm = TRUE), .groups = 'drop') %>%
arrange(desc(mean_r)) %>%
slice(1:8) %>%
pull(tissue)
top_tissues_auto <- combined_fold_data %>%
filter(target == "Autolysis") %>%
group_by(tissue) %>%
summarise(mean_r = mean(r_value, na.rm = TRUE), .groups = 'drop') %>%
arrange(desc(mean_r)) %>%
slice(1:8) %>%
pull(tissue)
fold_subset <- combined_fold_data %>%
filter((target == "RIN" & tissue %in% top_tissues_rin) |
(target == "Autolysis" & tissue %in% top_tissues_auto))
variability_plot <- ggplot(fold_subset, aes(x = tissue, y = r_value, fill = target)) +
geom_boxplot(alpha = 0.8, outlier.alpha = 0.6) +
facet_wrap(~target, scales = "free_x", ncol = 1) +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
legend.position = "none",
strip.text = element_text(size = 14, face = "bold"),
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 12, face = "bold")
) +
labs(
title = "B. Cross-Fold Performance Variability",
x = "Tissue Type",
y = "|R| Value Across Folds",
subtitle = "Box plots show distribution of performance across CV folds"
)
return(variability_plot)
}
create_stability_ranking <- function() {
stability_ranking <- combined_fold_data %>%
group_by(tissue, target) %>%
summarise(
mean_r = mean(r_value, na.rm = TRUE),
cv_r = sd(r_value, na.rm = TRUE) / mean(r_value, na.rm = TRUE),
.groups = 'drop'
) %>%
filter(is.finite(cv_r) & cv_r > 0) %>%
arrange(cv_r) %>%
mutate(
stability_rank = row_number(),
stability_category = cut(cv_r,
breaks = c(0, 0.1, 0.2, 0.5, Inf),
labels = c("Very Stable", "Stable", "Moderate", "Unstable"))
)
ranking_plot <- ggplot(stability_ranking, aes(x = stability_rank, y = cv_r, color = target)) +
geom_point(size = 3, alpha = 0.8) +
geom_hline(yintercept = c(0.1, 0.2, 0.5), linetype = "dashed", alpha = 0.6) +
scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "C. Model Stability Ranking",
x = "Stability Rank (1 = Most Stable)",
y = "Coefficient of Variation (|R|)",
color = "Model Type",
subtitle = "Horizontal lines indicate stability thresholds"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "none",
axis.title = element_text(size = 12, face = "bold")
)
return(ranking_plot)
}
panel_a <- create_stability_scatter()
panel_b <- create_fold_variability_boxplots()
panel_c <- create_stability_ranking()
top_panels <- grid.arrange(panel_a, panel_c, ncol = 2)
supp_fig_s2 <- grid.arrange(top_panels, panel_b, nrow = 2, heights = c(1, 1.5))
return(supp_fig_s2)
}
calculate_stability_stats <- function(all_results) {
stability_summary <- data.frame()
for(target in c("SMRIN", "SMATSSCR")) {
for(tissue in names(all_results[[target]])) {
result <- all_results[[target]][[tissue]]
if(!is.null(result) && !is.null(result$fold_results)) {
fold_r_values <- sapply(result$fold_results, function(x) abs(x$r))
stability_row <- data.frame(
tissue = tissue,
target = target,
mean_r = mean(fold_r_values),
cv_r = sd(fold_r_values) / mean(fold_r_values),
min_r = min(fold_r_values),
max_r = max(fold_r_values)
)
stability_summary <- rbind(stability_summary, stability_row)
}
}
}
by_target <- stability_summary %>%
group_by(target) %>%
summarise(
mean_cv = round(mean(cv_r, na.rm = TRUE), 3),
median_cv = round(median(cv_r, na.rm = TRUE), 3),
stable_models = sum(cv_r < 0.2, na.rm = TRUE),
total_models = n(),
.groups = 'drop'
)
return(stability_summary)
}
supp_figure_s2 <- create_cv_stability_analysis(all_results)


stability_stats <- calculate_stability_stats(all_results)
ggsave(file.path(output_path, "SuppFig_S2_CV_Stability.pdf"),
supp_figure_s2, width = 14, height = 12, dpi = 600, bg = "white")
#—————————————————————————— # 14. Supplementary Figure S3: Extended
Comparative Analysis #——————————————————————————
create_extended_comparative_analysis <- function(all_results, rin_summary, autolysis_summary) {
create_cross_model_correlation <- function() {
common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
comparison_data <- data.frame(
Tissue = common_tissues,
RIN_R = rin_summary$R[match(common_tissues, rin_summary$Tissue)],
Auto_R = autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)],
RIN_RMSE = rin_summary$RMSE[match(common_tissues, rin_summary$Tissue)],
Auto_RMSE = autolysis_summary$RMSE[match(common_tissues, autolysis_summary$Tissue)]
)
r_correlation <- cor(abs(comparison_data$RIN_R), abs(comparison_data$Auto_R), use = "complete.obs")
correlation_plot <- ggplot(comparison_data, aes(x = abs(RIN_R), y = abs(Auto_R))) +
geom_point(size = 3.5, alpha = 0.8, color = "darkblue") +
geom_smooth(method = "lm", se = TRUE, color = "red", alpha = 0.3) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "gray", size = 1) +
theme_minimal() +
labs(
title = "A. Cross-Model Performance Correlation",
x = "RIN Model |R|",
y = "Autolysis Model |R|",
subtitle = paste("Correlation =", round(r_correlation, 3))
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12, face = "bold")
) +
coord_fixed()
return(correlation_plot)
}
create_sample_size_performance <- function() {
combined_data <- rbind(
data.frame(rin_summary[, c("Tissue", "Samples", "R")], Model = "RIN"),
data.frame(autolysis_summary[, c("Tissue", "Samples", "R")], Model = "Autolysis")
)
combined_data$R_abs <- abs(combined_data$R)
combined_data$Size_Bin <- cut(combined_data$Samples,
breaks = c(0, 50, 100, 200, 500, Inf),
labels = c("≤50", "51-100", "101-200", "201-500", ">500"))
size_performance_plot <- ggplot(combined_data, aes(x = Size_Bin, y = R_abs, fill = Model)) +
geom_boxplot(alpha = 0.8, outlier.alpha = 0.6) +
scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
theme_minimal() +
labs(
title = "B. Model Performance by Sample Size Category",
x = "Sample Size Category",
y = "Absolute Correlation Coefficient |R|",
subtitle = "Performance distribution across sample size bins"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 12, face = "bold")
)
return(size_performance_plot)
}
create_tissue_preference_analysis <- function() {
common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
preference_data <- data.frame(
Tissue = common_tissues,
RIN_R = abs(rin_summary$R[match(common_tissues, rin_summary$Tissue)]),
Auto_R = abs(autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)])
)
preference_data$Better_Model <- ifelse(
preference_data$RIN_R > preference_data$Auto_R, "RIN", "Autolysis"
)
preference_data$Performance_Difference <- abs(preference_data$RIN_R - preference_data$Auto_R)
preference_data$Max_R <- pmax(preference_data$RIN_R, preference_data$Auto_R)
preference_data$Tissue_System <- case_when(
grepl("Brain|Nerve|Spinal", preference_data$Tissue) ~ "Nervous",
grepl("Heart|Artery|Aorta", preference_data$Tissue) ~ "Cardiovascular",
grepl("Lung|Trachea", preference_data$Tissue) ~ "Respiratory",
grepl("Liver|Pancreas|Stomach|Colon|Small|Esophagus", preference_data$Tissue) ~ "Digestive",
grepl("Kidney|Bladder", preference_data$Tissue) ~ "Urinary",
grepl("Muscle|Skin", preference_data$Tissue) ~ "Musculoskeletal",
grepl("Thyroid|Adrenal|Pituitary", preference_data$Tissue) ~ "Endocrine",
TRUE ~ "Other"
)
preference_plot <- ggplot(preference_data, aes(x = Max_R, y = Performance_Difference,
color = Better_Model, shape = Tissue_System)) +
geom_point(size = 4, alpha = 0.8) +
scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
scale_shape_manual(values = c(16, 17, 18, 15, 3, 4, 8, 1)[1:length(unique(preference_data$Tissue_System))]) +
theme_minimal() +
labs(
title = "C. Tissue Categories and Model Preference",
x = "Maximum |R| Value",
y = "Absolute Difference in |R|",
color = "Better Model",
shape = "Tissue System",
subtitle = "Model preference patterns across biological systems"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "right",
axis.title = element_text(size = 12, face = "bold")
)
return(preference_plot)
}
create_feature_overlap_analysis <- function() {
extract_features <- function(target_results, threshold = 5) {
feature_counts <- list()
for(tissue in names(target_results)) {
if(!is.null(target_results[[tissue]]$feature_counts)) {
counts <- target_results[[tissue]]$feature_counts
for(feat in names(counts)) {
if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
}
}
}
important_features <- names(feature_counts)[feature_counts >= threshold]
return(important_features)
}
thresholds <- c(3, 5, 10, 15, 20)
overlap_data <- data.frame()
for(thresh in thresholds) {
rin_thresh <- extract_features(all_results[["SMRIN"]], thresh)
auto_thresh <- extract_features(all_results[["SMATSSCR"]], thresh)
overlap_count <- length(intersect(rin_thresh, auto_thresh))
rin_only <- length(setdiff(rin_thresh, auto_thresh))
auto_only <- length(setdiff(auto_thresh, rin_thresh))
overlap_data <- rbind(overlap_data, data.frame(
Threshold = thresh,
Overlap = overlap_count,
RIN_Only = rin_only,
Auto_Only = auto_only
))
}
overlap_long <- reshape2::melt(overlap_data, id.vars = "Threshold",
variable.name = "Category", value.name = "Count")
overlap_plot <- ggplot(overlap_long, aes(x = factor(Threshold), y = Count, fill = Category)) +
geom_bar(stat = "identity", position = "stack") +
scale_fill_manual(values = c("Overlap" = "#8E44AD", "RIN_Only" = "#66CDAA", "Auto_Only" = "#FC8D62"),
labels = c("Common Features", "RIN Only", "Autolysis Only")) +
theme_minimal() +
labs(
title = "D. Feature Overlap at Different Selection Thresholds",
x = "Minimum Selection Frequency",
y = "Number of Features",
fill = "Feature Type",
subtitle = "Overlap analysis across selection frequency thresholds"
) +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
legend.position = "top",
axis.title = element_text(size = 12, face = "bold")
)
return(overlap_plot)
}
panel_a <- create_cross_model_correlation()
panel_b <- create_sample_size_performance()
panel_c <- create_tissue_preference_analysis()
panel_d <- create_feature_overlap_analysis()
combined_plot <- grid.arrange(panel_a, panel_b, panel_c, panel_d, nrow = 2, ncol = 2)
return(combined_plot)
}
create_model_comparison_summary <- function(rin_summary, autolysis_summary) {
common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
comparison_summary <- data.frame(
Tissue = common_tissues,
RIN_R = abs(rin_summary$R[match(common_tissues, rin_summary$Tissue)]),
Auto_R = abs(autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)])
)
comparison_summary$Better_Model <- ifelse(
comparison_summary$RIN_R > comparison_summary$Auto_R, "RIN", "Autolysis"
)
model_preference <- table(comparison_summary$Better_Model)
return(list(
summary = comparison_summary,
preference_counts = model_preference,
mean_performance = c(
RIN = mean(comparison_summary$RIN_R, na.rm = TRUE),
Autolysis = mean(comparison_summary$Auto_R, na.rm = TRUE)
)
))
}
supp_figure_s3 <- create_extended_comparative_analysis(all_results, rin_summary, autolysis_summary)

comparison_summary <- create_model_comparison_summary(rin_summary, autolysis_summary)
ggsave(file.path(output_path, "SuppFig_S3_Extended_Comparison.pdf"),
supp_figure_s3, width = 16, height = 14, dpi = 600, bg = "white")
G2;H2;Warningh in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
for '≤50' in 'mbcsToSbcs': <= substituted for ≤ (U+2264)g
#—————————————————————————— # 15. Comprehensive Final Report
Generation #——————————————————————————
generate_comprehensive_report <- function() {
report_path <- file.path(data_path, "processed/comprehensive_gtex_report.txt")
sink(report_path)
cat("================================================================================\n")
cat(" COMPREHENSIVE GTEx ANALYSIS REPORT \n")
cat("================================================================================\n\n")
# Header Information
cat("ANALYSIS OVERVIEW\n")
cat("-----------------\n")
cat(paste("Report Generated:", Sys.time(), "\n"))
cat(paste("Pipeline Version: GTEx Analysis Pipeline v1.0\n"))
cat(paste("Analysis Duration:", format(Sys.time() - start_time, digits = 3), "\n\n"))
# Dataset Summary
cat("DATASET SUMMARY\n")
cat("===============\n")
cat(paste("Total Samples Analyzed:", nrow(merged_gtex), "\n"))
cat(paste("Total Tissue Types:", length(unique(merged_gtex$tissue_main)), "\n"))
cat(paste("Total Features Extracted:", length(grep("^feature_", names(merged_gtex))), "\n"))
cat(paste("Male Subjects:", sum(merged_gtex$sex == "male"), "\n"))
cat(paste("Female Subjects:", sum(merged_gtex$sex == "female"), "\n\n"))
# Top tissue types by sample count
tissue_counts <- merged_gtex[, .N, by = tissue_main][order(-N)]
cat("Top 10 Tissue Types by Sample Count:\n")
for(i in 1:min(10, nrow(tissue_counts))) {
cat(sprintf(" %2d. %-25s: %4d samples\n", i, tissue_counts$tissue_main[i], tissue_counts$N[i]))
}
cat("\n")
# Quality Metrics Analysis
cat("QUALITY METRICS ANALYSIS\n")
cat("========================\n")
# RIN Analysis
rin_data <- merged_gtex[!is.na(SMRIN)]
if(nrow(rin_data) > 0) {
cat("RIN Score Distribution:\n")
cat(paste(" Samples with RIN data:", nrow(rin_data), "of", nrow(merged_gtex),
sprintf("(%.1f%%)\n", 100 * nrow(rin_data) / nrow(merged_gtex))))
cat(paste(" Mean RIN Score:", round(mean(rin_data$SMRIN), 2), "\n"))
cat(paste(" Median RIN Score:", round(median(rin_data$SMRIN), 2), "\n"))
cat(paste(" RIN Range:", round(min(rin_data$SMRIN), 2), "-", round(max(rin_data$SMRIN), 2), "\n"))
cat(paste(" High Quality (RIN ≥ 7.0):", sum(rin_data$SMRIN >= 7.0),
sprintf("(%.1f%%)\n", 100 * sum(rin_data$SMRIN >= 7.0) / nrow(rin_data))))
}
# Autolysis Analysis
autolysis_data <- merged_gtex[!is.na(SMATSSCR)]
if(nrow(autolysis_data) > 0) {
cat("\nAutolysis Score Distribution:\n")
cat(paste(" Samples with Autolysis data:", nrow(autolysis_data), "of", nrow(merged_gtex),
sprintf("(%.1f%%)\n", 100 * nrow(autolysis_data) / nrow(merged_gtex))))
cat(paste(" Mean Autolysis Score:", round(mean(autolysis_data$SMATSSCR), 2), "\n"))
cat(paste(" Median Autolysis Score:", round(median(autolysis_data$SMATSSCR), 2), "\n"))
autolysis_counts <- table(autolysis_data$SMATSSCR)
for(score in names(autolysis_counts)) {
cat(sprintf(" Score %s: %d samples (%.1f%%)\n", score, autolysis_counts[score],
100 * autolysis_counts[score] / nrow(autolysis_data)))
}
cat(paste(" High Quality (Score ≤ 2):", sum(autolysis_data$SMATSSCR <= 2),
sprintf("(%.1f%%)\n", 100 * sum(autolysis_data$SMATSSCR <= 2) / nrow(autolysis_data))))
}
# Dimensionality Reduction Results
cat("\nDIMENSIONALITY REDUCTION ANALYSIS\n")
cat("=================================\n")
if(exists("pca_results")) {
cat("Principal Component Analysis:\n")
cat(paste(" PC1 Variance Explained:", sprintf("%.1f%%", pca_results$variance_explained[1] * 100), "\n"))
cat(paste(" PC2 Variance Explained:", sprintf("%.1f%%", pca_results$variance_explained[2] * 100), "\n"))
cat(paste(" First 10 PCs Cumulative Variance:", sprintf("%.1f%%", pca_results$cumulative_variance[10] * 100), "\n"))
cat(paste(" First 50 PCs Cumulative Variance:", sprintf("%.1f%%", pca_results$cumulative_variance[50] * 100), "\n"))
}
if(exists("tissue_sil")) {
cat("\nUMAP Clustering Analysis:\n")
cat(paste(" Overall Silhouette Score:", sprintf("%.3f", mean(sil[,3], na.rm = TRUE)), "\n"))
cat(" Top 5 Best-Separated Tissues (by Silhouette Score):\n")
top_silhouette <- head(tissue_sil[order(-avg_silhouette)], 5)
for(i in 1:nrow(top_silhouette)) {
cat(sprintf(" %d. %-25s: %.3f\n", i, top_silhouette$tissue[i], top_silhouette$avg_silhouette[i]))
}
}
# Variance Analysis Results
cat("\nVARIANCE ANALYSIS (ANOVA)\n")
cat("=========================\n")
if(exists("rin_variance")) {
cat("RIN Score Variance Explained by Demographics:\n")
rin_variance_sorted <- rin_variance[order(-rin_variance$var_explained),]
for(i in 1:nrow(rin_variance_sorted)) {
cat(sprintf(" %-20s: %5.1f%% (p %s)\n",
rin_variance_sorted$label[i],
rin_variance_sorted$var_explained[i],
rin_variance_sorted$significance[i]))
}
}
if(exists("autolysis_variance")) {
cat("\nAutolysis Score Variance Explained by Demographics:\n")
autolysis_variance_sorted <- autolysis_variance[order(-autolysis_variance$var_explained),]
for(i in 1:nrow(autolysis_variance_sorted)) {
cat(sprintf(" %-20s: %5.1f%% (p %s)\n",
autolysis_variance_sorted$label[i],
autolysis_variance_sorted$var_explained[i],
autolysis_variance_sorted$significance[i]))
}
}
# Correlation Analysis
cat("\nCORRELATION ANALYSIS\n")
cat("====================\n")
if(exists("tissue_corr")) {
cat("RIN vs Autolysis Correlations by Tissue:\n")
tissue_corr_sorted <- tissue_corr[order(-abs(tissue_corr$correlation)),]
cat(" Strongest Correlations (Top 10):\n")
top_corr <- head(tissue_corr_sorted, 10)
for(i in 1:nrow(top_corr)) {
cat(sprintf(" %-25s: r = %6.3f (p %s, n = %d)\n",
top_corr$tissue_main[i],
top_corr$correlation[i],
top_corr$significance[i],
top_corr$n_samples[i]))
}
}
# Machine Learning Model Results
cat("\nMACHINE LEARNING MODEL PERFORMANCE\n")
cat("==================================\n")
if(exists("all_results")) {
# RIN Models
if("SMRIN" %in% names(all_results)) {
rin_models <- all_results[["SMRIN"]]
rin_r_values <- sapply(rin_models, function(x) {
if(is.null(x)) return(NA)
return(abs(x$performance$pooled_r)) # Use absolute R
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
})
rin_r_values <- rin_r_values[!is.na(rin_r_values)]
cat("RIN Score Prediction Models:\n")
cat(paste(" Successful models:", length(rin_r_values), "tissues\n"))
cat(paste(" Mean |R|:", sprintf("%.3f", mean(rin_r_values)), "\n"))
cat(paste(" Median |R|:", sprintf("%.3f", median(rin_r_values)), "\n"))
cat(paste(" Strong models (|R| > 0.5):", sum(rin_r_values > 0.5), "\n"))
cat(paste(" Excellent models (|R| > 0.7):", sum(rin_r_values > 0.7), "\n"))
# Top performing tissues
cat(" Top 5 RIN Prediction Models:\n")
top_rin <- names(sort(rin_r_values, decreasing = TRUE))[1:5]
for(i in 1:length(top_rin)) {
tissue_name <- top_rin[i]
r_value <- rin_r_values[tissue_name]
cat(sprintf(" %d. %-25s: |R| = %.3f\n", i, tissue_name, r_value))
}
}
# Autolysis Models
if("SMATSSCR" %in% names(all_results)) {
auto_models <- all_results[["SMATSSCR"]]
auto_r_values <- sapply(auto_models, function(x) {
if(is.null(x)) return(NA)
return(sqrt(x$performance$pooled_r2))
})
auto_r_values <- auto_r_values[!is.na(auto_r_values)]
cat("\nAutolysis Score Prediction Models:\n")
cat(paste(" Successful models:", length(auto_r_values), "tissues\n"))
cat(paste(" Mean R:", sprintf("%.3f", mean(auto_r_values)), "\n"))
cat(paste(" Median R:", sprintf("%.3f", median(auto_r_values)), "\n"))
cat(paste(" Strong models (R > 0.5):", sum(auto_r_values > 0.5), "\n"))
cat(paste(" Excellent models (R > 0.7):", sum(auto_r_values > 0.7), "\n"))
# Top performing tissues
cat(" Top 5 Autolysis Prediction Models:\n")
top_auto <- names(sort(auto_r_values, decreasing = TRUE))[1:5]
for(i in 1:length(top_auto)) {
tissue_name <- top_auto[i]
r_value <- auto_r_values[tissue_name]
cat(sprintf(" %d. %-25s: R = %.3f\n", i, tissue_name, r_value))
}
}
}
# Feature Analysis
cat("\nFEATURE IMPORTANCE ANALYSIS\n")
cat("===========================\n")
if(exists("common_features") && exists("rin_only_features") && exists("auto_only_features")) {
cat("Feature Overlap Analysis:\n")
cat(paste(" Common features (both models):", length(common_features), "\n"))
cat(paste(" RIN-specific features:", length(rin_only_features), "\n"))
cat(paste(" Autolysis-specific features:", length(auto_only_features), "\n"))
total_unique <- length(common_features) + length(rin_only_features) + length(auto_only_features)
cat(paste(" Feature overlap percentage:", sprintf("%.1f%%", 100 * length(common_features) / total_unique), "\n"))
}
# Esophagus Analysis
if(exists("esophagus_samples")) {
cat("\nESOPHAGUS SUBTYPE ANALYSIS\n")
cat("==========================\n")
esophagus_counts <- table(esophagus_samples$tissue_sub)
cat("Esophagus Subtype Distribution:\n")
for(subtype in names(esophagus_counts)) {
cat(sprintf(" %-30s: %3d samples\n", subtype, esophagus_counts[subtype]))
}
}
# Cross-Validation Stability
if(exists("stability_stats")) {
cat("\nMODEL STABILITY ANALYSIS\n")
cat("========================\n")
stable_rin <- sum(stability_stats$cv_r[stability_stats$target == "SMRIN"] < 0.2, na.rm = TRUE)
total_rin <- sum(stability_stats$target == "SMRIN")
stable_auto <- sum(stability_stats$cv_r[stability_stats$target == "SMATSSCR"] < 0.2, na.rm = TRUE)
total_auto <- sum(stability_stats$target == "SMATSSCR")
cat("Cross-Validation Stability (CV < 0.2 = Stable):\n")
cat(sprintf(" RIN Models: %d/%d stable (%.1f%%)\n", stable_rin, total_rin, 100 * stable_rin / total_rin))
cat(sprintf(" Autolysis Models: %d/%d stable (%.1f%%)\n", stable_auto, total_auto, 100 * stable_auto / total_auto))
}
# Summary Statistics
cat("\nSUMMARY STATISTICS\n")
cat("==================\n")
# Files generated
output_files <- list.files(output_path, pattern = "*.pdf", full.names = FALSE)
cat(paste("Generated", length(output_files), "visualization files:\n"))
for(file in output_files) {
cat(paste(" -", file, "\n"))
}
# Data files saved
processed_files <- list.files(file.path(data_path, "processed"), pattern = "*.rds|*.csv", full.names = FALSE)
cat(paste("\nSaved", length(processed_files), "processed data files:\n"))
for(file in head(processed_files, 10)) { # Show first 10 to avoid clutter
cat(paste(" -", file, "\n"))
}
if(length(processed_files) > 10) {
cat(paste(" ... and", length(processed_files) - 10, "more files\n"))
}
# Key Findings Summary
cat("\nKEY FINDINGS\n")
cat("============\n")
cat("1. DATASET CHARACTERISTICS:\n")
cat(" - Large-scale analysis of GTEx whole-slide image features\n")
cat(" - Comprehensive coverage of human tissue types\n")
cat(" - Quality metrics available for substantial subset\n\n")
cat("2. TISSUE HETEROGENEITY:\n")
cat(" - Clear tissue-specific clustering in UMAP analysis\n")
cat(" - Variable silhouette scores indicate different tissue separability\n")
cat(" - Feature importance varies significantly across tissue types\n\n")
cat("3. QUALITY PREDICTION MODELS:\n")
cat(" - Tissue-specific models show heterogeneous performance\n")
cat(" - Some tissues highly predictable, others more challenging\n")
cat(" - Cross-validation demonstrates model robustness\n\n")
cat("4. FEATURE ANALYSIS:\n")
cat(" - Statistical feature categories show different importance patterns\n")
cat(" - Partial overlap between RIN and autolysis predictive features\n")
cat(" - Feature consistency varies across tissues\n\n")
cat("5. BIOLOGICAL INSIGHTS:\n")
cat(" - Tissue type is strongest predictor of quality variance\n")
cat(" - Age and sex show modest but significant effects\n")
cat(" - Hardy scale (death circumstances) influences tissue quality\n\n")
# Technical Details
cat("TECHNICAL DETAILS\n")
cat("=================\n")
cat("Analysis Pipeline Components:\n")
cat(" 1. Data Integration and Quality Control\n")
cat(" 2. Demographic and Tissue Distribution Analysis\n")
cat(" 3. Principal Component Analysis (PCA)\n")
cat(" 4. Uniform Manifold Approximation and Projection (UMAP)\n")
cat(" 5. Silhouette Analysis for Cluster Validation\n")
cat(" 6. Quality Metrics Distribution Analysis\n")
cat(" 7. Analysis of Variance (ANOVA) for Demographic Effects\n")
cat(" 8. Correlation Analysis Between Quality Metrics\n")
cat(" 9. Tissue-Specific Predictive Modeling (Lasso Regression)\n")
cat(" 10. Cross-Validation and Model Stability Assessment\n")
cat(" 11. Feature Importance and Consistency Analysis\n")
cat(" 12. Residual Analysis and Model Diagnostics\n")
cat(" 13. Comparative Analysis Across Tissue Types\n\n")
cat("Statistical Methods:\n")
cat(" - 5-fold cross-validation for model training\n")
cat(" - Lasso regularization for feature selection\n")
cat(" - Spearman correlation for non-parametric associations\n")
cat(" - ANOVA for variance decomposition\n")
cat(" - Silhouette analysis for cluster quality assessment\n\n")
# Footer
cat("================================================================================\n")
cat(" END OF REPORT \n")
cat("================================================================================\n")
cat(paste("Report completed:", Sys.time(), "\n"))
cat("For questions or additional analysis, contact the analysis team.\n")
sink()
message("Comprehensive report generated successfully!")
message("Report saved to: ", report_path)
return(report_path)
}
# Execute the comprehensive report
start_time <- Sys.time() # Track analysis duration
comprehensive_report_path <- generate_comprehensive_report()
#—————————————————————————— # 15. Final Report
#——————————————————————————
# Generate comprehensive report
report_path <- file.path(data_path, "processed/tissue_modeling_report.txt")
sink(report_path)
cat("GTEx TISSUE-LEVEL MODELING REPORT\n")
cat("=================================\n\n")
cat("SUMMARY:\n")
cat(paste("Analysis Date:", Sys.time(), "\n"))
cat(paste("Total Tissues Analyzed:", length(unique(merged_gtex$tissue_main)), "\n"))
cat(paste("Total Samples:", nrow(merged_gtex), "\n\n"))
cat("MODELING RESULTS:\n")
# RIN Models
if("SMRIN" %in% names(all_results)) {
rin_models <- all_results[["SMRIN"]]
rin_r_values <- sapply(rin_models, function(x) {
if(is.null(x)) return(NA)
return(x$performance$pooled_r)
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
})
rin_r_values <- rin_r_values[!is.na(rin_r_values)]
cat("\nRIN Score Prediction:\n")
cat(paste(" Successful models:", length(rin_r_values), "\n"))
cat(paste(" Mean R:", round(mean(rin_r_values), 3), "\n"))
cat(paste(" Median R:", round(median(rin_r_values), 3), "\n"))
cat(paste(" Strong correlations (R > 0.5):", sum(rin_r_values > 0.5), "\n"))
}
# Autolysis Models
if("SMATSSCR" %in% names(all_results)) {
auto_models <- all_results[["SMATSSCR"]]
auto_r_values <- sapply(auto_models, function(x) {
if(is.null(x)) return(NA)
return(sqrt(x$performance$pooled_r2))
})
auto_r_values <- auto_r_values[!is.na(auto_r_values)]
cat("\nAutolysis Score Prediction:\n")
cat(paste(" Successful models:", length(auto_r_values), "\n"))
cat(paste(" Mean R:", round(mean(auto_r_values), 3), "\n"))
cat(paste(" Median R:", round(median(auto_r_values), 3), "\n"))
cat(paste(" Strong correlations (R > 0.5):", sum(auto_r_values > 0.5), "\n"))
}
cat("\nFEATURE ANALYSIS:\n")
cat(paste(" Common features between models:", length(common_features), "\n"))
cat(paste(" RIN-specific features:", length(rin_only_features), "\n"))
cat(paste(" Autolysis-specific features:", length(auto_only_features), "\n"))
cat("\nKEY FINDINGS:\n")
cat("1. Tissue-specific models show heterogeneous predictability\n")
cat("2. Feature importance varies across tissues and quality metrics\n")
cat("3. Cross-validation demonstrates model robustness\n")
cat("\n=================================\n")
cat("Report generated on:", as.character(Sys.time()), "\n")
sink()
message("Analysis complete! Report saved to:", report_path)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBHVEV4IFRpc3N1ZS1MZXZlbCBNb2RlbGluZyBQaXBlbGluZQojIFB1cnBvc2U6IFRpc3N1ZS1zcGVjaWZpYyBxdWFsaXR5IHByZWRpY3Rpb24gdXNpbmcgTGFzc28gcmVncmVzc2lvbgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDEuIFNldHVwCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgTG9hZCBkYXRhIG9uY2UKaWYoIWV4aXN0cygibWVyZ2VkX2d0ZXgiKSkgewogIG1lcmdlZF9ndGV4IDwtIHJlYWRSRFMoZmlsZS5wYXRoKGRhdGFfcGF0aCwgInByb2Nlc3NlZC9tZXJnZWRfZ3RleC5yZHMiKSkKfQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAyLiBXaXRoaW4tR3JvdXAgdnMgQmV0d2Vlbi1Hcm91cCBBbmFseXNpcyBmb3IgU2luZ2xlIFRpc3N1ZQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQojIEFuYWx5emUgYSBzcGVjaWZpYyB0aXNzdWUKdGlzc3VlX25hbWUgPC0gIkVzb3BoYWd1cyIKdGlzc3VlX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbdGlzc3VlX21haW4gPT0gdGlzc3VlX25hbWVdCnN1YnR5cGVzIDwtIHVuaXF1ZSh0aXNzdWVfZGF0YSR0aXNzdWVfc3ViKQoKaWYobGVuZ3RoKHN1YnR5cGVzKSA+IDEgJiYgbnJvdyh0aXNzdWVfZGF0YSkgPj0gMTApIHsKICAjIEV4dHJhY3QgYW5kIHNjYWxlIGZlYXR1cmVzCiAgZmVhdHVyZV9jb2xzIDwtIGdyZXAoIl5mZWF0dXJlXyIsIG5hbWVzKHRpc3N1ZV9kYXRhKSwgdmFsdWUgPSBUUlVFKQogIGZlYXR1cmVfbWF0cml4IDwtIHNjYWxlKGFzLm1hdHJpeCh0aXNzdWVfZGF0YVssIC4uZmVhdHVyZV9jb2xzXSkpCiAgCiAgIyBDYWxjdWxhdGUgZGlzdGFuY2UgbWF0cml4CiAgZGlzdF9tYXRyaXggPC0gZGlzdChmZWF0dXJlX21hdHJpeCkKICAKICAjIENhbGN1bGF0ZSB3aXRoaW4tZ3JvdXAgYW5kIGJldHdlZW4tZ3JvdXAgZGlzdGFuY2VzCiAgd2l0aGluX2Rpc3RhbmNlcyA8LSBsaXN0KCkKICBiZXR3ZWVuX2Rpc3RhbmNlcyA8LSBsaXN0KCkKICAKICBmb3Ioc3VidHlwZSBpbiBzdWJ0eXBlcykgewogICAgc3VidHlwZV9pbmRpY2VzIDwtIHdoaWNoKHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIgPT0gc3VidHlwZSkKICAgIAogICAgaWYobGVuZ3RoKHN1YnR5cGVfaW5kaWNlcykgPiAxKSB7CiAgICAgICMgV2l0aGluLWdyb3VwIGRpc3RhbmNlcwogICAgICBzdWJ0eXBlX2Rpc3QgPC0gYXMubWF0cml4KGRpc3RfbWF0cml4KVtzdWJ0eXBlX2luZGljZXMsIHN1YnR5cGVfaW5kaWNlc10KICAgICAgd2l0aGluX2Rpc3RhbmNlc1tbc3VidHlwZV1dIDwtIHN1YnR5cGVfZGlzdFt1cHBlci50cmkoc3VidHlwZV9kaXN0KV0KICAgICAgCiAgICAgICMgQmV0d2Vlbi1ncm91cCBkaXN0YW5jZXMKICAgICAgb3RoZXJfaW5kaWNlcyA8LSB3aGljaCh0aXNzdWVfZGF0YSR0aXNzdWVfc3ViICE9IHN1YnR5cGUpCiAgICAgIGlmKGxlbmd0aChvdGhlcl9pbmRpY2VzKSA+IDApIHsKICAgICAgICBiZXR3ZWVuX2Rpc3RzIDwtIGFzLm1hdHJpeChkaXN0X21hdHJpeClbc3VidHlwZV9pbmRpY2VzLCBvdGhlcl9pbmRpY2VzXQogICAgICAgIGJldHdlZW5fZGlzdGFuY2VzW1tzdWJ0eXBlXV0gPC0gYXMudmVjdG9yKGJldHdlZW5fZGlzdHMpCiAgICAgIH0KICAgIH0KICB9CiAgCiAgIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgYWxsX3dpdGhpbiA8LSB1bmxpc3Qod2l0aGluX2Rpc3RhbmNlcykKICBhbGxfYmV0d2VlbiA8LSB1bmxpc3QoYmV0d2Vlbl9kaXN0YW5jZXMpCiAgCiAgc2ltaWxhcml0eV9yZXN1bHRzIDwtIGxpc3QoCiAgICB0aXNzdWUgPSB0aXNzdWVfbmFtZSwKICAgIHN1YnR5cGVzID0gc3VidHlwZXMsCiAgICBzYW1wbGVzX3Blcl9zdWJ0eXBlID0gdGFibGUodGlzc3VlX2RhdGEkdGlzc3VlX3N1YiksCiAgICBzdW1tYXJ5ID0gZGF0YS5mcmFtZSgKICAgICAgY29tcGFyaXNvbiA9IGMoIldpdGhpbiBTdWJ0eXBlIiwgIkJldHdlZW4gU3VidHlwZXMiKSwKICAgICAgbWVhbl9kaXN0YW5jZSA9IGMobWVhbihhbGxfd2l0aGluKSwgbWVhbihhbGxfYmV0d2VlbikpLAogICAgICBtZWRpYW5fZGlzdGFuY2UgPSBjKG1lZGlhbihhbGxfd2l0aGluKSwgbWVkaWFuKGFsbF9iZXR3ZWVuKSkKICAgICksCiAgICByYXRpbyA9IG1lYW4oYWxsX2JldHdlZW4pIC8gbWVhbihhbGxfd2l0aGluKQogICkKICAKICAjIFBFUk1BTk9WQSB0ZXN0CiAgcGVybV90ZXN0IDwtIGFkb25pczIoZGlzdF9tYXRyaXggfiB0aXNzdWVfc3ViLCBkYXRhID0gdGlzc3VlX2RhdGEpCiAgc3RhdGlzdGljYWxfcmVzdWx0cyA8LSBsaXN0KAogICAgdGlzc3VlID0gdGlzc3VlX25hbWUsCiAgICBwZXJtYW5vdmEgPSBwZXJtX3Rlc3QsCiAgICByX3NxdWFyZWQgPSBwZXJtX3Rlc3QkUjJbMV0sCiAgICBwX3ZhbHVlID0gcGVybV90ZXN0JGBQcig+RilgWzFdLAogICAgc2lnbmlmaWNhbnQgPSBwZXJtX3Rlc3QkYFByKD5GKWBbMV0gPCAwLjA1CiAgKQogIAogICMgQ3JlYXRlIHZpc3VhbGl6YXRpb25zCiAgcGNhX3Jlc3VsdCA8LSBwcmNvbXAoZmVhdHVyZV9tYXRyaXgpCiAgcGNhX3Bsb3QgPC0gZ2dwbG90KGRhdGEuZnJhbWUoUEMxID0gcGNhX3Jlc3VsdCR4WywxXSwgUEMyID0gcGNhX3Jlc3VsdCR4WywyXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3VidHlwZSA9IHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIpLCAKICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gU3VidHlwZSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAzKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJQQ0Egb2YiLCB0aXNzdWVfbmFtZSwgImJ5IFN1YnR5cGUiKSkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfc3VidHlwZV9wY2EucGRmIikpLCAKICAgICAgICAgcGNhX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICAjIFVNQVAgdmlzdWFsaXphdGlvbgogIHNldC5zZWVkKDEyMykKICB1bWFwX3Jlc3VsdCA8LSB1d290Ojp1bWFwKGZlYXR1cmVfbWF0cml4LCBuX25laWdoYm9ycyA9IG1pbigxNSwgbnJvdyhmZWF0dXJlX21hdHJpeCktMSkpCiAgCiAgdW1hcF9wbG90IDwtIGdncGxvdChkYXRhLmZyYW1lKFVNQVAxID0gdW1hcF9yZXN1bHRbLDFdLCBVTUFQMiA9IHVtYXBfcmVzdWx0WywyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3VidHlwZSA9IHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIpLAogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gU3VidHlwZSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAzKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJVTUFQIG9mIiwgdGlzc3VlX25hbWUsICJieSBTdWJ0eXBlIikpCiAgCiAgZ2dzYXZlKGZpbGUucGF0aChvdXRwdXRfcGF0aCwgcGFzdGUwKHRpc3N1ZV9uYW1lLCAiX3N1YnR5cGVfdW1hcC5wZGYiKSksIAogICAgICAgICB1bWFwX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICAjIFNhdmUgcmVzdWx0cwogIHNhdmVSRFMoc2ltaWxhcml0eV9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfZGlzdGFuY2VfYW5hbHlzaXMucmRzIikpKQogIHNhdmVSRFMoc3RhdGlzdGljYWxfcmVzdWx0cywgZmlsZS5wYXRoKHJlc3VsdHNfcGF0aCwgcGFzdGUwKHRpc3N1ZV9uYW1lLCAiX3N0YXRpc3RpY2FsX3Rlc3RzLnJkcyIpKSkKfQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAzLiBUaXNzdWUtTGV2ZWwgTGFzc28gTW9kZWxpbmcKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KIyBTZXQgcGFyYW1ldGVycwprX2ZvbGRzIDwtIDUKdGFyZ2V0cyA8LSBjKCJTTVJJTiIsICJTTUFUU1NDUiIpCnRpc3N1ZXMgPC0gdW5pcXVlKG1lcmdlZF9ndGV4JHRpc3N1ZV9tYWluKQphbGxfcmVzdWx0cyA8LSBsaXN0KCkKCiMgUHJvY2VzcyBlYWNoIHRhcmdldApmb3IodGFyZ2V0IGluIHRhcmdldHMpIHsKICB0YXJnZXRfcmVzdWx0cyA8LSBsaXN0KCkKICBtZXNzYWdlKHBhc3RlKCJcblByb2Nlc3NpbmcgdGFyZ2V0OiIsIHRhcmdldCkpCiAgCiAgIyBQcm9jZXNzIGVhY2ggdGlzc3VlCiAgZm9yKHRpc3N1ZV9uYW1lIGluIHRpc3N1ZXMpIHsKICAgIG1lc3NhZ2UocGFzdGUoIlxuQW5hbHl6aW5nIiwgdGlzc3VlX25hbWUsICJmb3IiLCB0YXJnZXQpKQogICAgCiAgICAjIEV4dHJhY3QgYW5kIGZpbHRlciBkYXRhCiAgICB0aXNzdWVfZGF0YSA8LSBtZXJnZWRfZ3RleFt0aXNzdWVfbWFpbiA9PSB0aXNzdWVfbmFtZV0KICAgIHRpc3N1ZV9kYXRhIDwtIHRpc3N1ZV9kYXRhWyFpcy5uYSh0aXNzdWVfZGF0YVtbdGFyZ2V0XV0pXQogICAgCiAgICBpZihucm93KHRpc3N1ZV9kYXRhKSA8IDEwKSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIkluc3VmZmljaWVudCBzYW1wbGVzIGZvciIsIHRpc3N1ZV9uYW1lKSkKICAgICAgbmV4dAogICAgfQogICAgCiAgICAjIEZlYXR1cmUgc2VsZWN0aW9uCiAgICBmZWF0dXJlX2NvbHMgPC0gZ3JlcCgiXmZlYXR1cmVfIiwgbmFtZXModGlzc3VlX2RhdGEpLCB2YWx1ZSA9IFRSVUUpCiAgICBmZWF0dXJlX3ZhcnMgPC0gYXBwbHkodGlzc3VlX2RhdGFbLCAuLmZlYXR1cmVfY29sc10sIDIsIHZhcikKICAgIHZhbGlkX2ZlYXR1cmVzIDwtIGZlYXR1cmVfY29sc1tmZWF0dXJlX3ZhcnMgPiAxZS0xMF0KICAgIAogICAgIyBDcmVhdGUgY3Jvc3MtdmFsaWRhdGlvbiBmb2xkcwogICAgc2V0LnNlZWQoMTIzKQogICAgZm9sZHMgPC0gY3JlYXRlRm9sZHModGlzc3VlX2RhdGFbW3RhcmdldF1dLCBrID0ga19mb2xkcykKICAgIAogICAgIyBDcm9zcy12YWxpZGF0aW9uCiAgICBmb2xkX3Jlc3VsdHMgPC0gbGlzdCgpCiAgICBhbGxfZm9sZF9wcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKCkKICAgIAogICAgZm9yKGkgaW4gMTprX2ZvbGRzKSB7CiAgICAgICMgU3BsaXQgZGF0YQogICAgICB0ZXN0X2luZGljZXMgPC0gZm9sZHNbW2ldXQogICAgICB0cmFpbl9kYXRhIDwtIHRpc3N1ZV9kYXRhWy10ZXN0X2luZGljZXNdCiAgICAgIHRlc3RfZGF0YSA8LSB0aXNzdWVfZGF0YVt0ZXN0X2luZGljZXNdCiAgICAgIAogICAgICAjIEZlYXR1cmUgc2VsZWN0aW9uIC0gdG9wIDUlIGNvcnJlbGF0ZWQKICAgICAgZmVhdHVyZV9jb3JyZWxhdGlvbnMgPC0gc2FwcGx5KHZhbGlkX2ZlYXR1cmVzLCBmdW5jdGlvbihjb2wpIHsKICAgICAgICBjb3IodHJhaW5fZGF0YVtbY29sXV0sIHRyYWluX2RhdGFbW3RhcmdldF1dLCB1c2UgPSAiY29tcGxldGUub2JzIikKICAgICAgfSkKICAgICAgCiAgICAgIHRvcF9uX2ZlYXR1cmVzIDwtIGNlaWxpbmcobGVuZ3RoKHZhbGlkX2ZlYXR1cmVzKSAqIDAuMDUpCiAgICAgIHRvcF9mZWF0dXJlcyA8LSBuYW1lcyhzb3J0KGFicyhmZWF0dXJlX2NvcnJlbGF0aW9ucyksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTp0b3Bfbl9mZWF0dXJlc10KICAgICAgCiAgICAgICMgUHJlcGFyZSBtYXRyaWNlcwogICAgICB4X3RyYWluIDwtIGFzLm1hdHJpeCh0cmFpbl9kYXRhWywgLi50b3BfZmVhdHVyZXNdKQogICAgICB5X3RyYWluIDwtIHRyYWluX2RhdGFbW3RhcmdldF1dCiAgICAgIHhfdHJhaW5fc2NhbGVkIDwtIHNjYWxlKHhfdHJhaW4pCiAgICAgIHhfbWVhbiA8LSBhdHRyKHhfdHJhaW5fc2NhbGVkLCAic2NhbGVkOmNlbnRlciIpCiAgICAgIHhfc2QgPC0gYXR0cih4X3RyYWluX3NjYWxlZCwgInNjYWxlZDpzY2FsZSIpCiAgICAgIAogICAgICAjIFRyYWluIG1vZGVsCiAgICAgIGN2X2ZpdCA8LSBjdi5nbG1uZXQoeF90cmFpbl9zY2FsZWQsIHlfdHJhaW4sIGFscGhhID0gMSkKICAgICAgYmVzdF9sYW1iZGEgPC0gY3ZfZml0JGxhbWJkYS5taW4KICAgICAgbGFzc29fbW9kZWwgPC0gZ2xtbmV0KHhfdHJhaW5fc2NhbGVkLCB5X3RyYWluLCBhbHBoYSA9IDEsIGxhbWJkYSA9IGJlc3RfbGFtYmRhKQogICAgICAKICAgICAgIyBNYWtlIHByZWRpY3Rpb25zCiAgICAgIHhfdGVzdCA8LSBhcy5tYXRyaXgodGVzdF9kYXRhWywgLi50b3BfZmVhdHVyZXNdKQogICAgICB4X3Rlc3Rfc2NhbGVkIDwtIHNjYWxlKHhfdGVzdCwgY2VudGVyID0geF9tZWFuLCBzY2FsZSA9IHhfc2QpCiAgICAgIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobGFzc29fbW9kZWwsIHhfdGVzdF9zY2FsZWQsIHMgPSBiZXN0X2xhbWJkYSkKICAgICAgCiAgICAgICMgU3RvcmUgcHJlZGljdGlvbnMKICAgICAgZm9sZF9wcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKAogICAgICAgIGZvbGQgPSBpLAogICAgICAgIHNhbXBsZV9pZCA9IHRlc3RfZGF0YSRzYW1wbGVfaWQsCiAgICAgICAgYWN0dWFsID0gdGVzdF9kYXRhW1t0YXJnZXRdXSwKICAgICAgICBwcmVkaWN0ZWQgPSBhcy5udW1lcmljKHByZWRpY3Rpb25zKQogICAgICApCiAgICAgIGFsbF9mb2xkX3ByZWRpY3Rpb25zIDwtIHJiaW5kKGFsbF9mb2xkX3ByZWRpY3Rpb25zLCBmb2xkX3ByZWRpY3Rpb25zKQogICAgICAKICAgICAgIyBDYWxjdWxhdGUgbWV0cmljcwogICAgICBybXNlIDwtIHNxcnQobWVhbigocHJlZGljdGlvbnMgLSB0ZXN0X2RhdGFbW3RhcmdldF1dKV4yKSkKICAgICAgcl92YWx1ZSA8LSBjb3IocHJlZGljdGlvbnMsIHRlc3RfZGF0YVtbdGFyZ2V0XV0pIAogICAgICByMiA8LSBjb3IocHJlZGljdGlvbnMsIHRlc3RfZGF0YVtbdGFyZ2V0XV0pXjIKICAgICAgbWFlIDwtIG1lYW4oYWJzKHByZWRpY3Rpb25zIC0gdGVzdF9kYXRhW1t0YXJnZXRdXSkpCiAgICAgIAogICAgICBmb2xkX3Jlc3VsdHNbW2ldXSA8LSBsaXN0KAogICAgICAgIGZvbGQgPSBpLAogICAgICAgIHJtc2UgPSBybXNlLAogICAgICAgIHIgPSByX3ZhbHVlLAogICAgICAgIHIyID0gcjIsCiAgICAgICAgbWFlID0gbWFlLAogICAgICAgIGxhbWJkYSA9IGJlc3RfbGFtYmRhLAogICAgICAgIG1vZGVsID0gbGFzc29fbW9kZWwsCiAgICAgICAgZmVhdHVyZXMgPSB0b3BfZmVhdHVyZXMKICAgICAgKQogICAgfQogICAgCiAgICAjIENhbGN1bGF0ZSBvdmVyYWxsIHBlcmZvcm1hbmNlCiAgICBwb29sZWRfcm1zZSA8LSBzcXJ0KG1lYW4oKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCAtIGFsbF9mb2xkX3ByZWRpY3Rpb25zJGFjdHVhbCleMikpCiAgICBwb29sZWRfciA8LSBjb3IoYWxsX2ZvbGRfcHJlZGljdGlvbnMkcHJlZGljdGVkLCBhbGxfZm9sZF9wcmVkaWN0aW9ucyRhY3R1YWwpICAjIDwtLSBSLCBub3QgUsKyCiAgICBwb29sZWRfcjIgPC0gY29yKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCwgYWxsX2ZvbGRfcHJlZGljdGlvbnMkYWN0dWFsKV4yCiAgICBwb29sZWRfbWFlIDwtIG1lYW4oYWJzKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCAtIGFsbF9mb2xkX3ByZWRpY3Rpb25zJGFjdHVhbCkpCiAgICAKICAgICMgQ291bnQgZmVhdHVyZSBzZWxlY3Rpb24gZnJlcXVlbmN5CiAgICBhbGxfc2VsZWN0ZWRfZmVhdHVyZXMgPC0gdW5saXN0KGxhcHBseShmb2xkX3Jlc3VsdHMsIGZ1bmN0aW9uKHgpIHgkZmVhdHVyZXMpKQogICAgZmVhdHVyZV9jb3VudHMgPC0gdGFibGUoYWxsX3NlbGVjdGVkX2ZlYXR1cmVzKQogICAgY29uc2lzdGVudF9mZWF0dXJlcyA8LSBuYW1lcyhmZWF0dXJlX2NvdW50c1tmZWF0dXJlX2NvdW50cyA+PSAzXSkKICAgIAogICAgIyBTdG9yZSByZXN1bHRzCiAgICB0aXNzdWVfbW9kZWxfcmVzdWx0IDwtIGxpc3QoCiAgICAgIHRpc3N1ZSA9IHRpc3N1ZV9uYW1lLAogICAgICB0YXJnZXQgPSB0YXJnZXQsCiAgICAgIHBlcmZvcm1hbmNlID0gbGlzdCgKICAgICAgICBwb29sZWRfcm1zZSA9IHBvb2xlZF9ybXNlLAogICAgICAgIHBvb2xlZF9yID0gcG9vbGVkX3IsCiAgICAgICAgcG9vbGVkX3IyID0gcG9vbGVkX3IyLAogICAgICAgIHBvb2xlZF9tYWUgPSBwb29sZWRfbWFlCiAgICAgICksCiAgICAgIGZvbGRfcmVzdWx0cyA9IGZvbGRfcmVzdWx0cywKICAgICAgZmVhdHVyZV9jb3VudHMgPSBmZWF0dXJlX2NvdW50cywKICAgICAgY29uc2lzdGVudF9mZWF0dXJlcyA9IGNvbnNpc3RlbnRfZmVhdHVyZXMsCiAgICAgIHNhbXBsZV9zaXplID0gbnJvdyh0aXNzdWVfZGF0YSksCiAgICAgIGFsbF9wcmVkaWN0aW9ucyA9IGFsbF9mb2xkX3ByZWRpY3Rpb25zCiAgICApCiAgICAKICAgIHRhcmdldF9yZXN1bHRzW1t0aXNzdWVfbmFtZV1dIDwtIHRpc3N1ZV9tb2RlbF9yZXN1bHQKICAgIAogICAgIyBDcmVhdGUgdmlzdWFsaXphdGlvbiBmb3IgZ29vZCBtb2RlbHMgKHVzaW5nIFIgPiAwLjU1IHdoaWNoIGlzIGFwcHJveGltYXRlbHkgUsKyID4gMC4zKQogICMgaWYocG9vbGVkX3IyID4gMC4zKSB7ICAjIHVzZSBpZiBSwrIgaXMgcmVxdWlyZWQKICAgIGlmKGFicyhwb29sZWRfcikgPiAwLjU1KSB7CiAgICAgIHNjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoYWxsX2ZvbGRfcHJlZGljdGlvbnMsIGFlcyh4ID0gYWN0dWFsLCB5ID0gcHJlZGljdGVkKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDIpICsKICAgICAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gImJsdWUiKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHRpdGxlID0gcGFzdGUodGlzc3VlX25hbWUsICItIiwgdGFyZ2V0LCAiUHJlZGljdGlvbiIpLAogICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiUiA9Iiwgcm91bmQocG9vbGVkX3IsIDMpLCAiUk1TRSA9Iiwgcm91bmQocG9vbGVkX3Jtc2UsIDMpKSwKICAgICAgICAjIHN1YnRpdGxlID0gcGFzdGUoIlLCsiA9Iiwgcm91bmQocG9vbGVkX3IyLCAzKSwgIlJNU0UgPSIsIHJvdW5kKHBvb2xlZF9ybXNlLCAzKSksICMgdXNlIGlmIFLCsiBpcyByZXF1aXJlZAogICAgICAgICAgeCA9IHBhc3RlKCJBY3R1YWwiLCB0YXJnZXQpLAogICAgICAgICAgeSA9IHBhc3RlKCJQcmVkaWN0ZWQiLCB0YXJnZXQpCiAgICAgICAgKSArCiAgICAgICAgdGhlbWVfbWluaW1hbCgpCiAgICAgIAogICAgICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfIiwgdGFyZ2V0LCAiX3NjYXR0ZXIucGRmIikpLCAKICAgICAgICAgICAgIHNjYXR0ZXJfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQogICAgfQogIH0KICAKICBhbGxfcmVzdWx0c1tbdGFyZ2V0XV0gPC0gdGFyZ2V0X3Jlc3VsdHMKICBzYXZlUkRTKHRhcmdldF9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCBwYXN0ZTAodGFyZ2V0LCAiX21vZGVsX3Jlc3VsdHMucmRzIikpKQp9CgpzYXZlUkRTKGFsbF9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCAiYWxsX3Rpc3N1ZV9tb2RlbHNfY29tcGxldGUucmRzIikpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDQuIE1vZGVsIFBlcmZvcm1hbmNlIFN1bW1hcnkKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KIyBDcmVhdGUgcGVyZm9ybWFuY2Ugc3VtbWFyeQpjcmVhdGVfcGVyZm9ybWFuY2Vfc3VtbWFyeSA8LSBmdW5jdGlvbihyZXN1bHRzLCB0YXJnZXRfbmFtZSkgewogIHN1bW1hcnlfZGYgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yKHRpc3N1ZSBpbiBuYW1lcyhyZXN1bHRzKSkgewogICAgdGlzc3VlX3Jlc3VsdCA8LSByZXN1bHRzW1t0aXNzdWVdXQogICAgaWYoaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgbmV4dAogICAgCiAgICByb3cgPC0gZGF0YS5mcmFtZSgKICAgICAgVGlzc3VlID0gdGlzc3VlLAogICAgICBTYW1wbGVzID0gdGlzc3VlX3Jlc3VsdCRzYW1wbGVfc2l6ZSwKICAgICAgUk1TRSA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UsIDMpLAogICAgICBSID0gcm91bmQodGlzc3VlX3Jlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfciwgMyksCiAgICAjIFJfc3F1YXJlZCA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IyLCAzKSwgIyB1c2UgaWYgUsKyIGlzIHJlcXVpcmVkCiAgICAgIE1BRSA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX21hZSwgMyksCiAgICAgIENvbnNpc3RlbnRfRmVhdHVyZXMgPSBsZW5ndGgodGlzc3VlX3Jlc3VsdCRjb25zaXN0ZW50X2ZlYXR1cmVzKQogICAgKQogICAgc3VtbWFyeV9kZiA8LSByYmluZChzdW1tYXJ5X2RmLCByb3cpCiAgfQogIAogIHN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZltvcmRlcihhYnMoc3VtbWFyeV9kZiRSKSwgZGVjcmVhc2luZyA9IFRSVUUpLF0gICMgU29ydCBieSBhYnNvbHV0ZSBSCiAgc3VtbWFyeV9kZiRQZXJmb3JtYW5jZV9DYXRlZ29yeSA8LSBjdXQoYWJzKHN1bW1hcnlfZGYkUiksICAjIFVzZSBhYnNvbHV0ZSBSIGZvciBjYXRlZ29yaWVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygtSW5mLCAwLjMxNiwgMC41NDgsIDAuNzA3LCBJbmYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJQb29yIiwgIk1vZGVyYXRlIiwgIkdvb2QiLCAiRXhjZWxsZW50IikpICAKCiMgc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmW29yZGVyKHN1bW1hcnlfZGYkUl9zcXVhcmVkLCBkZWNyZWFzaW5nID0gVFJVRSksXSAjIFNvcnQgYnkgUsKyCiMgc3VtbWFyeV9kZiRQZXJmb3JtYW5jZV9DYXRlZ29yeSA8LSBjdXQoc3VtbWFyeV9kZiRSX3NxdWFyZWQsICMgVXNlIFLCsiBmb3IgY2F0ZWdvcmllcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgYnJlYWtzID0gYygtSW5mLCAwLjEsIDAuMywgMC41LCBJbmYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgIGxhYmVscyA9IGMoIlBvb3IiLCAiTW9kZXJhdGUiLCAiR29vZCIsICJFeGNlbGxlbnQiKSkKICAKICByZXR1cm4oc3VtbWFyeV9kZikKfQoKIyBDcmVhdGUgc3VtbWFyaWVzCnJpbl9zdW1tYXJ5IDwtIGNyZWF0ZV9wZXJmb3JtYW5jZV9zdW1tYXJ5KGFsbF9yZXN1bHRzW1siU01SSU4iXV0sICJSSU4gU2NvcmUiKQphdXRvbHlzaXNfc3VtbWFyeSA8LSBjcmVhdGVfcGVyZm9ybWFuY2Vfc3VtbWFyeShhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dLCAiQXV0b2x5c2lzIFNjb3JlIikKCiMgU2F2ZSBzdW1tYXJpZXMKd3JpdGUuY3N2KHJpbl9zdW1tYXJ5LCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCAicmluX21vZGVsX3N1bW1hcnkuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoYXV0b2x5c2lzX3N1bW1hcnksIGZpbGUucGF0aChyZXN1bHRzX3BhdGgsICJhdXRvbHlzaXNfbW9kZWxfc3VtbWFyeS5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCgojIENyZWF0ZSBjb21wYXJpc29uCm1vZGVsX2NvbXBhcmlzb24gPC0gbWVyZ2UoCiAgcmluX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUiIpXSwKICBhdXRvbHlzaXNfc3VtbWFyeVssIGMoIlRpc3N1ZSIsICJSIildLAogIGJ5ID0gIlRpc3N1ZSIsCiAgc3VmZml4ZXMgPSBjKCJfUklOIiwgIl9BdXRvIikKKQptb2RlbF9jb21wYXJpc29uJEJldHRlcl9Nb2RlbCA8LSBpZmVsc2UoYWJzKG1vZGVsX2NvbXBhcmlzb24kUl9SSU4pID4gYWJzKG1vZGVsX2NvbXBhcmlzb24kUl9BdXRvKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJJTiIsICJBdXRvbHlzaXMiKQoKIyBDcmVhdGUgY29tcGFyaXNvbiB3aGVuIGlmIHJlcXVpcmVkIGZvciBSwrIKIyBtb2RlbF9jb21wYXJpc29uIDwtIG1lcmdlKAojICAgcmluX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUl9zcXVhcmVkIildLAojICAgYXV0b2x5c2lzX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUl9zcXVhcmVkIildLAojICAgYnkgPSAiVGlzc3VlIiwKIyAgIHN1ZmZpeGVzID0gYygiX1JJTiIsICJfQXV0byIpCiMgKQojIG1vZGVsX2NvbXBhcmlzb24kQmV0dGVyX01vZGVsIDwtIGlmZWxzZShtb2RlbF9jb21wYXJpc29uJFJfc3F1YXJlZF9SSU4gPiBtb2RlbF9jb21wYXJpc29uJFJfc3F1YXJlZF9BdXRvLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSSU4iLCAiQXV0b2x5c2lzIikKd3JpdGUuY3N2KG1vZGVsX2NvbXBhcmlzb24sIGZpbGUucGF0aChyZXN1bHRzX3BhdGgsICJtb2RlbF9jb21wYXJpc29uLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgNS4gRmlndXJlIDMgLSBNb2RlbCBQZXJmb3JtYW5jZSBWaXN1YWxpemF0aW9uCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgUGFuZWwgQSAmIEI6IFBlcmZvcm1hbmNlIGJhciBwbG90cwpjcmVhdGVfaG9yaXpvbnRhbF9iYXJwbG90IDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzLCBzY29yZV90eXBlLCBwYW5lbF9sYWJlbCkgewogIHJfdmFsdWVzIDwtIHNhcHBseShhbGxfcmVzdWx0c1tbc2NvcmVfdHlwZV1dLCBmdW5jdGlvbih4KSB7CiAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICAgIHJldHVybih4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKQogICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICB9KQogIAogIHBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKAogICAgVGlzc3VlID0gbmFtZXMocl92YWx1ZXMpLAogICAgUiA9IHJfdmFsdWVzCiAgKVtvcmRlcigtcl92YWx1ZXMpLCBdCiAgCiAgcGxvdF9kYXRhJFRpc3N1ZSA8LSBmYWN0b3IocGxvdF9kYXRhJFRpc3N1ZSwgbGV2ZWxzID0gcmV2KHBsb3RfZGF0YSRUaXNzdWUpKQogIAogIGlmKHNjb3JlX3R5cGUgPT0gIlNNUklOIikgewogICAgY29sb3JfZ3JhZGllbnQgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjRThGNUU4IiwgIiM2NmNkYWEiLCAiIzJFOEI1NyIpKSgxMDApCiAgfSBlbHNlIHsKICAgIGNvbG9yX2dyYWRpZW50IDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiI0ZGRjJFNiIsICIjRkM4RDYyIiwgIiNEOTQ4MDEiKSkoMTAwKQogIH0KICAKICBiYXJfcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFIsIHkgPSBUaXNzdWUsIGZpbGwgPSBSKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC44KSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4zZiIsIFIpLCB4ID0gUi8yKSwgCiAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsIHNpemUgPSAzLjUsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvcl9ncmFkaWVudCwgZ3VpZGUgPSAibm9uZSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKHRpdGxlID0gcGFuZWxfbGFiZWwsIHggPSAiQ29ycmVsYXRpb24gQ29lZmZpY2llbnQgKFIpIiwgeSA9ICIiKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMCksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpCiAgICApCiAgCiAgcmV0dXJuKGJhcl9wbG90KQp9CgpyaW5fYmFycGxvdCA8LSBjcmVhdGVfaG9yaXpvbnRhbF9iYXJwbG90KGFsbF9yZXN1bHRzLCAiU01SSU4iLCAiQSIpCmF1dG9seXNpc19iYXJwbG90IDwtIGNyZWF0ZV9ob3Jpem9udGFsX2JhcnBsb3QoYWxsX3Jlc3VsdHMsICJTTUFUU1NDUiIsICJCIikKCiMgUGFuZWwgQzogUklOIHNjYXR0ZXIgcGxvdHMKY3JlYXRlX3NjYXR0ZXJfcGFuZWwgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHRvcF90aXNzdWVzKSB7CiAgcGxvdHMgPC0gbGlzdCgpCiAgCiAgZm9yKGkgaW4gMTozKSB7CiAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXVtbdG9wX3Rpc3N1ZXNbaV1dXQogICAgaWYoaXMubnVsbChyZXN1bHQpKSBuZXh0CiAgICAKICAgIGFsbF9wcmVkcyA8LSByZXN1bHQkYWxsX3ByZWRpY3Rpb25zCiAgICByX3ZhbHVlIDwtIHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfcgogICMgcl92YWx1ZSA8LSBzcXJ0KHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogICAgcm1zZV92YWx1ZSA8LSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UKICAgIAogICAgcCA8LSBnZ3Bsb3QoYWxsX3ByZWRzLCBhZXMoeCA9IGFjdHVhbCwgeSA9IHByZWRpY3RlZCkpICsKICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgY29sb3IgPSAiIzY2Y2RhYSIsIHNpemUgPSAxLjUpICsKICAgICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmx1ZSIpICsKICAgICAgZ2d0aXRsZShwYXN0ZTAodG9wX3Rpc3N1ZXNbaV0sICIgfCBSID0gIiwgcm91bmQocl92YWx1ZSwgMykpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKQogICAgCiAgICBwbG90c1tbaV1dIDwtIHAKICB9CiAgCiAgcmV0dXJuKHBsb3RzKQp9CgojIEdldCB0b3AgMyB0aXNzdWVzCmFsbF9yX3JpbiA8LSBzYXBwbHkoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSwgZnVuY3Rpb24oeCkgewogIGlmKGlzLm51bGwoeCkpIHJldHVybigwKQogIHJldHVybihhYnMoeCRwZXJmb3JtYW5jZSRwb29sZWRfcikpICAjIFVzZSBhYnNvbHV0ZSB2YWx1ZSBmb3Igc29ydGluZwojIHJldHVybihzcXJ0KHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkgIyB1c2Ugd2hlbiByZXF1aXJlZCBmb3IgUsKyCn0pCnRvcF9yaW5fdGlzc3VlcyA8LSBuYW1lcyhzb3J0KGFsbF9yX3JpbiwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjNdCgpyaW5fc2NhdHRlcl9wbG90cyA8LSBjcmVhdGVfc2NhdHRlcl9wYW5lbChhbGxfcmVzdWx0cywgdG9wX3Jpbl90aXNzdWVzKQpyaW5fZ3JpZCA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSByaW5fc2NhdHRlcl9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbSA9IHRleHRHcm9iKCJBY3R1YWwgUklOIFNjb3JlIiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZnQgPSB0ZXh0R3JvYigiUHJlZGljdGVkIFJJTiBTY29yZSIsIHJvdCA9IDkwLCBncCA9IGdwYXIoZm9udHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkMiLCB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSkpCgojIFBhbmVsIEQ6IEF1dG9seXNpcyBib3ggcGxvdHMKY3JlYXRlX2JveHBsb3RfcGFuZWwgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHRvcF90aXNzdWVzKSB7CiAgcGxvdHMgPC0gbGlzdCgpCiAgCiAgZm9yKGkgaW4gMTozKSB7CiAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbWyJTTUFUU1NDUiJdXVtbdG9wX3Rpc3N1ZXNbaV1dXQogICAgaWYoaXMubnVsbChyZXN1bHQpKSBuZXh0CiAgICAKICAgIGFsbF9wcmVkcyA8LSByZXN1bHQkYWxsX3ByZWRpY3Rpb25zCiAgICBhbGxfcHJlZHMkYWN0dWFsX2ZhY3RvciA8LSBmYWN0b3IoYWxsX3ByZWRzJGFjdHVhbCwgbGV2ZWxzID0gYygwLCAxLCAyLCAzKSkKICAgIHJfdmFsdWUgPC0gc3FydChyZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKQogICAgcm1zZV92YWx1ZSA8LSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UKICAgIAogICAgcCA8LSBnZ3Bsb3QoYWxsX3ByZWRzLCBhZXMoeCA9IGFjdHVhbF9mYWN0b3IsIHkgPSBwcmVkaWN0ZWQpKSArCiAgICAgIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGFjdHVhbF9mYWN0b3IpLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC4yLCBjb2xvciA9ICIjMjE5MDhkIikgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIwIiA9ICIjZTBlMGUwIiwgIjEiID0gIiNiZGQ3ZTciLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIiM5ZWNhZTEiLCAiMyIgPSAiIzZiYWVkNiIpKSArCiAgICAgIGdndGl0bGUocGFzdGUwKHRvcF90aXNzdWVzW2ldLCAiIHwgUiA9ICIsIHJvdW5kKHJfdmFsdWUsIDMpKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgICAKICAgIHBsb3RzW1tpXV0gPC0gcAogIH0KICAKICByZXR1cm4ocGxvdHMpCn0KCmFsbF9yX2F1dG8gPC0gc2FwcGx5KGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sIGZ1bmN0aW9uKHgpIHsKICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICByZXR1cm4oc3FydCh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikpCn0pCnRvcF9hdXRvX3Rpc3N1ZXMgPC0gbmFtZXMoc29ydChhbGxfcl9hdXRvLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6M10KCmF1dG9fYm94cGxvdF9wbG90cyA8LSBjcmVhdGVfYm94cGxvdF9wYW5lbChhbGxfcmVzdWx0cywgdG9wX2F1dG9fdGlzc3VlcykKYXV0b2x5c2lzX2dyaWQgPC0gZ3JpZC5hcnJhbmdlKGdyb2JzID0gYXV0b19ib3hwbG90X3Bsb3RzLCBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tID0gdGV4dEdyb2IoIkFjdHVhbCBDYXRlZ29yeSIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0ID0gdGV4dEdyb2IoIlByZWRpY3RlZCBTY29yZSIsIHJvdCA9IDkwLCBncCA9IGdwYXIoZm9udHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkQiLCB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSkpCgojIENvbWJpbmUgYWxsIHBhbmVscwpjb21iaW5lZF9iYXJwbG90cyA8LSBncmlkLmFycmFuZ2UocmluX2JhcnBsb3QsIGF1dG9seXNpc19iYXJwbG90LCBuY29sID0gMikKY29tYmluZWRfQ0RfcGxvdHMgPC0gZ3JpZC5hcnJhbmdlKHJpbl9ncmlkLCBhdXRvbHlzaXNfZ3JpZCwgbnJvdyA9IDIpCmZpZ3VyZTNfZmluYWwgPC0gZ3JpZC5hcnJhbmdlKGNvbWJpbmVkX2JhcnBsb3RzLCBjb21iaW5lZF9DRF9wbG90cywgbmNvbCA9IDIsIHdpZHRocyA9IGMoMC40NSwgMC41NSkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlM19GaW5hbF9Db21wbGV0ZS5wZGYiKSwgCiAgICAgICBmaWd1cmUzX2ZpbmFsLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxMiwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDYuIEZpZ3VyZSA0IC0gRmVhdHVyZSBJbXBvcnRhbmNlCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgRXh0cmFjdCB0b3AgZmVhdHVyZXMKZXh0cmFjdF9mZWF0dXJlcyA8LSBmdW5jdGlvbihyZXN1bHRzLCB0YXJnZXQsIHRvcF9uID0gMjApIHsKICBhbGxfdGlzc3VlX2ZlYXR1cmVzIDwtIGRhdGEuZnJhbWUoKQogIAogIGZvcih0aXNzdWUgaW4gbmFtZXMocmVzdWx0c1tbdGFyZ2V0XV0pKSB7CiAgICB0aXNzdWVfcmVzdWx0IDwtIHJlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgaWYoaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgbmV4dAogICAgCiAgICBpZighaXMubnVsbCh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKSkgewogICAgICBmZWF0dXJlcyA8LSBuYW1lcyh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKQogICAgICBjb3VudHMgPC0gYXMubnVtZXJpYyh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKQogICAgICAKICAgICAgdGlzc3VlX2RmIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgZmVhdHVyZSA9IGZlYXR1cmVzLAogICAgICAgIGNvdW50ID0gY291bnRzLAogICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgKQogICAgICBhbGxfdGlzc3VlX2ZlYXR1cmVzIDwtIHJiaW5kKGFsbF90aXNzdWVfZmVhdHVyZXMsIHRpc3N1ZV9kZikKICAgIH0KICB9CiAgCiAgZmVhdHVyZV9zdW1tYXJ5IDwtIGFsbF90aXNzdWVfZmVhdHVyZXMgJT4lCiAgICBncm91cF9ieShmZWF0dXJlKSAlPiUKICAgIHN1bW1hcml6ZSh0b3RhbF9jb3VudCA9IHN1bShjb3VudCkpICU+JQogICAgYXJyYW5nZShkZXNjKHRvdGFsX2NvdW50KSkgJT4lCiAgICBzbGljZSgxOnRvcF9uKQogIAogIGZlYXR1cmVfbWF0cml4IDwtIGFsbF90aXNzdWVfZmVhdHVyZXMgJT4lCiAgICBmaWx0ZXIoZmVhdHVyZSAlaW4lIGZlYXR1cmVfc3VtbWFyeSRmZWF0dXJlKSAlPiUKICAgIHJlc2hhcGUyOjpkY2FzdChmZWF0dXJlIH4gdGlzc3VlLCB2YWx1ZS52YXIgPSAiY291bnQiLCBmaWxsID0gMCkKICAKICByZXR1cm4obGlzdChzdW1tYXJ5ID0gZmVhdHVyZV9zdW1tYXJ5LCBtYXRyaXggPSBmZWF0dXJlX21hdHJpeCkpCn0KCnJpbl9mZWF0dXJlcyA8LSBleHRyYWN0X2ZlYXR1cmVzKGFsbF9yZXN1bHRzLCAiU01SSU4iKQphdXRvX2ZlYXR1cmVzIDwtIGV4dHJhY3RfZmVhdHVyZXMoYWxsX3Jlc3VsdHMsICJTTUFUU1NDUiIpCgojIEZpbmQgY29tbW9uIGZlYXR1cmVzCmNvbW1vbl9mZWF0dXJlcyA8LSBpbnRlcnNlY3QocmluX2ZlYXR1cmVzJHN1bW1hcnkkZmVhdHVyZSwgYXV0b19mZWF0dXJlcyRzdW1tYXJ5JGZlYXR1cmUpCnJpbl9vbmx5X2ZlYXR1cmVzIDwtIHNldGRpZmYocmluX2ZlYXR1cmVzJHN1bW1hcnkkZmVhdHVyZSwgYXV0b19mZWF0dXJlcyRzdW1tYXJ5JGZlYXR1cmUpCmF1dG9fb25seV9mZWF0dXJlcyA8LSBzZXRkaWZmKGF1dG9fZmVhdHVyZXMkc3VtbWFyeSRmZWF0dXJlLCByaW5fZmVhdHVyZXMkc3VtbWFyeSRmZWF0dXJlKQoKIyBDcmVhdGUgaGVhdG1hcHMKY3JlYXRlX2hlYXRtYXAgPC0gZnVuY3Rpb24oZmVhdHVyZV9kYXRhLCBjb2xvcl9wYWxldHRlKSB7CiAgZmVhdHVyZV9sb25nIDwtIHJlc2hhcGUyOjptZWx0KGZlYXR1cmVfZGF0YSRtYXRyaXgsIGlkLnZhcnMgPSAiZmVhdHVyZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5uYW1lID0gInRpc3N1ZSIsIHZhbHVlLm5hbWUgPSAiY291bnQiKQogIAogIGZlYXR1cmVfbG9uZyA8LSBtZXJnZShmZWF0dXJlX2xvbmcsIGZlYXR1cmVfZGF0YSRzdW1tYXJ5WywgYygiZmVhdHVyZSIsICJ0b3RhbF9jb3VudCIpXSwgYnkgPSAiZmVhdHVyZSIpCiAgZmVhdHVyZV9sb25nJGZlYXR1cmVfbGFiZWwgPC0gc3ViKCJmZWF0dXJlXyIsICIiLCBmZWF0dXJlX2xvbmckZmVhdHVyZSkKICAKICBwIDwtIGdncGxvdChmZWF0dXJlX2xvbmcsIGFlcyh4ID0gdGlzc3VlLCB5ID0gcmVvcmRlcihmZWF0dXJlX2xhYmVsLCB0b3RhbF9jb3VudCkpKSArCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBjb3VudCksIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuMSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gY29sb3JfcGFsZXR0ZSwgbmFtZSA9ICJTZWxlY3Rpb25cbkZyZXF1ZW5jeSIpICsKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiRmVhdHVyZSBJRCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2NSwgaGp1c3QgPSAxLCBzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpCiAgICApCiAgCiAgcmV0dXJuKHApCn0KCnJpbl9oZWF0bWFwIDwtIGNyZWF0ZV9oZWF0bWFwKHJpbl9mZWF0dXJlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICIjNjZDREFBIiwgIiMwMDY0MDAiKSkoMTAwKSkgKwogIGdndGl0bGUoIkEiKQoKYXV0b19oZWF0bWFwIDwtIGNyZWF0ZV9oZWF0bWFwKGF1dG9fZmVhdHVyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCAiI0ZDOEQ2MiIsICIjRDk0ODAxIikpKDEwMCkpICsKICBnZ3RpdGxlKCJCIikKCiMgQ3JlYXRlIFZlbm4gZGlhZ3JhbQp2ZW5uX3Bsb3QgPC0gZ2dwbG90KCkgKwogIGdnZm9yY2U6Omdlb21fY2lyY2xlKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKC0xLCAxKSwgeSA9IGMoMCwgMCksIHIgPSBjKDIsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnkgPSBjKCJSSU4gRmVhdHVyZXMiLCAiQXV0b2x5c2lzIEZlYXR1cmVzIikpLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHgwID0geCwgeTAgPSB5LCByID0gciwgZmlsbCA9IGNhdGVnb3J5KSwKICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjgpICsKICBnZW9tX3RleHQoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoLTEuNjUsIDAsIDEuNjUpLCB5ID0gYygwLCAwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBjKGxlbmd0aChyaW5fb25seV9mZWF0dXJlcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoKGF1dG9fb25seV9mZWF0dXJlcykpKSwKICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSwgc2l6ZSA9IDEwLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiBGZWF0dXJlcyIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMgRmVhdHVyZXMiID0gIiNGQzhENjIiKSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgeGxpbSgtMywgMykgKyB5bGltKC0yLCAyKSArCiAgZ2d0aXRsZSgiQyIpCgojIENvbWJpbmUgcGxvdHMKaGVhdG1hcHMgPC0gcGxvdF9ncmlkKHJpbl9oZWF0bWFwLCBhdXRvX2hlYXRtYXAsIG5jb2wgPSAyKQpmaWd1cmU0IDwtIHBsb3RfZ3JpZChoZWF0bWFwcywgdmVubl9wbG90LCBucm93ID0gMiwgcmVsX2hlaWdodHMgPSBjKDIsIDEuNSkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlNF9GZWF0dXJlLnBkZiIpLCAKICAgICAgIGZpZ3VyZTQsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEyLCBkcGkgPSA2MDAsIGJnID0gInRyYW5zcGFyZW50IikKYGBgCgoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDcuIEFkZGl0aW9uYWwgVmlzdWFsaXphdGlvbnMgYW5kIENvbXBhcmF0aXZlIEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgQ3JlYXRlIGNvbXBhcmF0aXZlIHBlcmZvcm1hbmNlIGNoYXJ0IGZvciB0b3AgdGlzc3VlcwpjcmVhdGVfY29tcGFyYXRpdmVfYmFyX2NoYXJ0IDwtIGZ1bmN0aW9uKHJpbl9yZXN1bHRzLCBhdXRvbHlzaXNfcmVzdWx0cykgewogIGNvbW1vbl90aXNzdWVzIDwtIGludGVyc2VjdChyaW5fcmVzdWx0cyRUaXNzdWUsIGF1dG9seXNpc19yZXN1bHRzJFRpc3N1ZSkKICAKICBjb21wYXJpc29uX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgIFRpc3N1ZSA9IGNvbW1vbl90aXNzdWVzLAogICAgUklOX1IgPSByaW5fcmVzdWx0cyRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fcmVzdWx0cyRUaXNzdWUpXSwKICAgIEF1dG9seXNpc19SID0gYXV0b2x5c2lzX3Jlc3VsdHMkUlttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3Jlc3VsdHMkVGlzc3VlKV0KICApCiAgCiAgY29tcGFyaXNvbl9kYXRhJEF2ZXJhZ2VfUiA8LSAoYWJzKGNvbXBhcmlzb25fZGF0YSRSSU5fUikgKyBhYnMoY29tcGFyaXNvbl9kYXRhJEF1dG9seXNpc19SKSkgLyAyCiAgY29tcGFyaXNvbl9kYXRhIDwtIGNvbXBhcmlzb25fZGF0YVtvcmRlcigtY29tcGFyaXNvbl9kYXRhJEF2ZXJhZ2VfUiksIF0KICB0b3BfdGlzc3VlcyA8LSBoZWFkKGNvbXBhcmlzb25fZGF0YSwgMTUpCiAgCiAgcGxvdF9kYXRhIDwtIHJlc2hhcGUyOjptZWx0KHRvcF90aXNzdWVzWywgYygiVGlzc3VlIiwgIlJJTl9SIiwgIkF1dG9seXNpc19SIildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gIlRpc3N1ZSIsIHZhcmlhYmxlLm5hbWUgPSAiTW9kZWwiLCB2YWx1ZS5uYW1lID0gIlJfdmFsdWUiKQogIHBsb3RfZGF0YSRUaXNzdWUgPC0gZmFjdG9yKHBsb3RfZGF0YSRUaXNzdWUsIGxldmVscyA9IHRvcF90aXNzdWVzJFRpc3N1ZSkKICAKICBjb21wYXJpc29uX3Bsb3QgPC0gZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBUaXNzdWUsIHkgPSBSX3ZhbHVlLCBmaWxsID0gTW9kZWwpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuM2YiLCBSX3ZhbHVlKSksIAogICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHZqdXN0ID0gLTAuMywgc2l6ZSA9IDMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTl9SIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpc19SIiA9ICIjRkM4RDYyIiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlJJTiIsICJBdXRvbHlzaXMiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIKICAgICkgKwogICAgbGFicygKICAgICAgdGl0bGUgPSAiTW9kZWwgUGVyZm9ybWFuY2UgQ29tcGFyaXNvbiBBY3Jvc3MgVG9wIFRpc3N1ZXMiLAogICAgICBzdWJ0aXRsZSA9ICJUb3AgMTUgdGlzc3VlcyByYW5rZWQgYnkgYXZlcmFnZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCIsCiAgICAgIHggPSAiVGlzc3VlIFR5cGUiLAogICAgICB5ID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IChSKSIsCiAgICAgIGZpbGwgPSAiTW9kZWwgVHlwZSIKICAgICkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiY29tcGFyYXRpdmVfcGVyZm9ybWFuY2VfY2hhcnQucGRmIiksIAogICAgICAgICBjb21wYXJpc29uX3Bsb3QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICByZXR1cm4oY29tcGFyaXNvbl9wbG90KQp9CgojIENyZWF0ZSBjb3JyZWxhdGlvbiBkaXN0cmlidXRpb24gYW5hbHlzaXMKY3JlYXRlX2NvcnJlbGF0aW9uX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbihyZXN1bHRzX2RmLCB0YXJnZXRfbmFtZSkgewogIHJfZGlzdF9wbG90IDwtIGdncGxvdChyZXN1bHRzX2RmLCBhZXMoeCA9IGFicyhSKSkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wNSwgZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44KSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihhYnMoUikpKSwgCiAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMikgKwogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihhYnMoUikpKSwgCiAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMikgKwogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWVhbihhYnMocmVzdWx0c19kZiRSKSkgKyAwLjA4LCB5ID0gbWF4KHRhYmxlKGN1dChhYnMocmVzdWx0c19kZiRSKSwgMjApKSkgKiAwLjgsIAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTWVhbiA9Iiwgcm91bmQobWVhbihhYnMocmVzdWx0c19kZiRSKSksIDMpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtZWRpYW4oYWJzKHJlc3VsdHNfZGYkUikpIC0gMC4wOCwgeSA9IG1heCh0YWJsZShjdXQoYWJzKHJlc3VsdHNfZGYkUiksIDIwKSkpICogMC42LCAKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk1lZGlhbiA9Iiwgcm91bmQobWVkaWFuKGFicyhyZXN1bHRzX2RmJFIpKSwgMykpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9IHBhc3RlKCJEaXN0cmlidXRpb24gb2YgQ29ycmVsYXRpb24gQ29lZmZpY2llbnRzOiIsIHRhcmdldF9uYW1lKSwKICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiQW5hbHlzaXMgb2YiLCBucm93KHJlc3VsdHNfZGYpLCAidGlzc3VlLXNwZWNpZmljIG1vZGVscyIpLAogICAgICB4ID0gIkFic29sdXRlIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IHxSfCIsCiAgICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAoZ3N1YigiICIsICJfIiwgdG9sb3dlcih0YXJnZXRfbmFtZSkpLCAiX2NvcnJlbGF0aW9uX2Rpc3RyaWJ1dGlvbi5wZGYiKSksIAogICAgICAgICByX2Rpc3RfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNywgZHBpID0gMzAwKQogIAogIHJldHVybihyX2Rpc3RfcGxvdCkKfQoKIyBDcmVhdGUgZW5oYW5jZWQgc2NhdHRlciBwbG90cyB3aXRoIHJlc2lkdWFsIGFuYWx5c2lzCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzIDwtIGZ1bmN0aW9uKHJlc3VsdHMsIHRhcmdldF9uYW1lLCB0b3BfbiA9IDYpIHsKICBhbGxfcl92YWx1ZXMgPC0gc2FwcGx5KHJlc3VsdHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybigwKQogICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkKICB9KQogIAogIHRvcF90aXNzdWVzIDwtIG5hbWVzKHNvcnQoYWxsX3JfdmFsdWVzLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6bWluKHRvcF9uLCBsZW5ndGgoYWxsX3JfdmFsdWVzKSldCiAgCiAgZm9yKHRpc3N1ZSBpbiB0b3BfdGlzc3VlcykgewogICAgaWYoaXMubnVsbChyZXN1bHRzW1t0aXNzdWVdXSkpIG5leHQKICAgIAogICAgcHJlZGljdGlvbnMgPC0gcmVzdWx0c1tbdGlzc3VlXV0kYWxsX3ByZWRpY3Rpb25zCiAgICByX3ZhbHVlIDwtIHJlc3VsdHNbW3Rpc3N1ZV1dJHBlcmZvcm1hbmNlJHBvb2xlZF9yCiAgICBybXNlX3ZhbHVlIDwtIHJlc3VsdHNbW3Rpc3N1ZV1dJHBlcmZvcm1hbmNlJHBvb2xlZF9ybXNlCiAgICAKICAgIHByZWRpY3Rpb25zJHJlc2lkdWFsIDwtIHByZWRpY3Rpb25zJHByZWRpY3RlZCAtIHByZWRpY3Rpb25zJGFjdHVhbAogICAgCiAgICBzY2F0dGVyX3Bsb3QgPC0gZ2dwbG90KHByZWRpY3Rpb25zLCBhZXMoeCA9IGFjdHVhbCwgeSA9IHByZWRpY3RlZCkpICsKICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgY29sb3IgPSAiIzJFODZBQiIsIHNpemUgPSAyLjUpICsKICAgICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gImRhcmtibHVlIiwgYWxwaGEgPSAwLjMpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9IHBhc3RlKHRpc3N1ZSwgIi0iLCB0YXJnZXRfbmFtZSwgIlByZWRpY3Rpb24gTW9kZWwiKSwKICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJSID0iLCByb3VuZChyX3ZhbHVlLCAzKSwgInwgUk1TRSA9Iiwgcm91bmQocm1zZV92YWx1ZSwgMykpLAogICAgICAgIHggPSBwYXN0ZSgiQWN0dWFsIiwgdGFyZ2V0X25hbWUpLAogICAgICAgIHkgPSBwYXN0ZSgiUHJlZGljdGVkIiwgdGFyZ2V0X25hbWUpCiAgICAgICkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIGdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsIHBhc3RlMCgiZW5oYW5jZWRfIiwgdGlzc3VlLCAiXyIsIGdzdWIoIiAiLCAiXyIsIHRvbG93ZXIodGFyZ2V0X25hbWUpKSwgIl9zY2F0dGVyLnBkZiIpKSwgCiAgICAgICAgICAgc2NhdHRlcl9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCiAgfQp9CgojIEV4ZWN1dGUgY29tcGFyYXRpdmUgdmlzdWFsaXphdGlvbnMKY29tcGFyaXNvbl9jaGFydCA8LSBjcmVhdGVfY29tcGFyYXRpdmVfYmFyX2NoYXJ0KHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkKcmluX2Rpc3RyaWJ1dGlvbiA8LSBjcmVhdGVfY29ycmVsYXRpb25fZGlzdHJpYnV0aW9uKHJpbl9zdW1tYXJ5LCAiUklOIFNjb3JlIikKYXV0b2x5c2lzX2Rpc3RyaWJ1dGlvbiA8LSBjcmVhdGVfY29ycmVsYXRpb25fZGlzdHJpYnV0aW9uKGF1dG9seXNpc19zdW1tYXJ5LCAiQXV0b2x5c2lzIFNjb3JlIikKCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sICJSSU4gU2NvcmUiLCB0b3BfbiA9IDYpCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sICJBdXRvbHlzaXMgU2NvcmUiLCB0b3BfbiA9IDYpCmBgYAoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA4LiBGaWd1cmUgNTogTW9kZWwgUGVyZm9ybWFuY2UgdnMgU2FtcGxlIFNpemUgQW5hbHlzaXMKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KY3JlYXRlX3NhbXBsZV9zaXplX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkgewogIAogIGNvbWJpbmVkX2RhdGEgPC0gcmJpbmQoCiAgICBkYXRhLmZyYW1lKAogICAgICBUaXNzdWUgPSByaW5fc3VtbWFyeSRUaXNzdWUsCiAgICAgIFNhbXBsZXMgPSByaW5fc3VtbWFyeSRTYW1wbGVzLCAKICAgICAgUl92YWx1ZSA9IGFicyhyaW5fc3VtbWFyeSRSKSwKICAgICAgTW9kZWwgPSAiUklOIgogICAgKSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIFRpc3N1ZSA9IGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSwKICAgICAgU2FtcGxlcyA9IGF1dG9seXNpc19zdW1tYXJ5JFNhbXBsZXMsCiAgICAgIFJfdmFsdWUgPSBhYnMoYXV0b2x5c2lzX3N1bW1hcnkkUiksCiAgICAgIE1vZGVsID0gIkF1dG9seXNpcyIKICAgICkKICApCiAgCiAgc2NhdHRlcl9wYW5lbCA8LSBnZ3Bsb3QoY29tYmluZWRfZGF0YSwgYWVzKHggPSBTYW1wbGVzLCB5ID0gUl92YWx1ZSwgY29sb3IgPSBNb2RlbCkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSwgYWxwaGEgPSAwLjgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIHNpemUgPSAxLjIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9ICJBIiwKICAgICAgeCA9ICJTYW1wbGUgU2l6ZSAobikiLAogICAgICB5ID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IHxSfCIsCiAgICAgIHN1YnRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNhbXBsZSBzaXplIGFuZCBtb2RlbCBwZXJmb3JtYW5jZSIKICAgICkgKwogICAgdGhlbWUoCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDApLAogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICApCiAgCiAgY29tYmluZWRfZGF0YSRzaXplX2NhdGVnb3J5IDwtIGN1dChjb21iaW5lZF9kYXRhJFNhbXBsZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgNTAsIDEwMCwgMjAwLCA1MDAsIEluZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygi4omkNTAiLCAiNTEtMTAwIiwgIjEwMS0yMDAiLCAiMjAxLTUwMCIsICI+NTAwIikpCiAgCiAgYmluX3N1bW1hcnkgPC0gY29tYmluZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KHNpemVfY2F0ZWdvcnksIE1vZGVsKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgbWVhbl9yID0gbWVhbihSX3ZhbHVlLCBuYS5ybSA9IFRSVUUpLAogICAgICBzZV9yID0gc2QoUl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIHNxcnQobigpKSwKICAgICAgdGlzc3VlX2NvdW50ID0gbigpLAogICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICApCiAgCiAgYmluX3BhbmVsIDwtIGdncGxvdChiaW5fc3VtbWFyeSwgYWVzKHggPSBzaXplX2NhdGVnb3J5LCB5ID0gbWVhbl9yLCBmaWxsID0gTW9kZWwpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuODUsIHdpZHRoID0gMC43KSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWVhbl9yIC0gc2VfciwgeW1heCA9IG1lYW5fciArIHNlX3IpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43KSwgd2lkdGggPSAwLjI1LCBzaXplID0gMC44KSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGlzc3VlX2NvdW50KSwgCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNyksIAogICAgICAgICAgICAgIHZqdXN0ID0gLTEuNSwgc2l6ZSA9IDMuNSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkIiLAogICAgICB4ID0gIlNhbXBsZSBTaXplIENhdGVnb3JpZXMiLAogICAgICB5ID0gIk1lYW4gfFJ8IMKxIFNFIiwKICAgICAgc3VidGl0bGUgPSAiUGVyZm9ybWFuY2Ugc3RyYXRpZmllZCBieSBzYW1wbGUgc2l6ZSAobnVtYmVycyBzaG93IHRpc3N1ZSBjb3VudCkiCiAgICApICsKICAgIHRoZW1lKAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwKSwKICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgKQogIAogIGZpZ3VyZTVfY29tYmluZWQgPC0gZ3JpZC5hcnJhbmdlKHNjYXR0ZXJfcGFuZWwsIGJpbl9wYW5lbCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMSwgMSkpCiAgCiAgcmV0dXJuKGZpZ3VyZTVfY29tYmluZWQpCn0KCmFuYWx5emVfc2FtcGxlX3NpemVfZWZmZWN0cyA8LSBmdW5jdGlvbihjb21iaW5lZF9kYXRhKSB7CiAgcmluX2RhdGEgPC0gY29tYmluZWRfZGF0YVtjb21iaW5lZF9kYXRhJE1vZGVsID09ICJSSU4iLCBdCiAgYXV0b19kYXRhIDwtIGNvbWJpbmVkX2RhdGFbY29tYmluZWRfZGF0YSRNb2RlbCA9PSAiQXV0b2x5c2lzIiwgXQogIAogIHJpbl9jb3IgPC0gY29yLnRlc3QocmluX2RhdGEkU2FtcGxlcywgcmluX2RhdGEkUl92YWx1ZSkKICBhdXRvX2NvciA8LSBjb3IudGVzdChhdXRvX2RhdGEkU2FtcGxlcywgYXV0b19kYXRhJFJfdmFsdWUpCiAgCiAgcmV0dXJuKGxpc3QocmluX3Rlc3QgPSByaW5fY29yLCBhdXRvbHlzaXNfdGVzdCA9IGF1dG9fY29yKSkKfQoKZmlndXJlNV9wbG90IDwtIGNyZWF0ZV9zYW1wbGVfc2l6ZV9hbmFseXNpcyhyaW5fc3VtbWFyeSwgYXV0b2x5c2lzX3N1bW1hcnkpCnNhbXBsZV9zaXplX3N0YXRzIDwtIGFuYWx5emVfc2FtcGxlX3NpemVfZWZmZWN0cyhyYmluZCgKICBkYXRhLmZyYW1lKFRpc3N1ZSA9IHJpbl9zdW1tYXJ5JFRpc3N1ZSwgU2FtcGxlcyA9IHJpbl9zdW1tYXJ5JFNhbXBsZXMsIAogICAgICAgICAgICAgUl92YWx1ZSA9IGFicyhyaW5fc3VtbWFyeSRSKSwgTW9kZWwgPSAiUklOIiksCiAgZGF0YS5mcmFtZShUaXNzdWUgPSBhdXRvbHlzaXNfc3VtbWFyeSRUaXNzdWUsIFNhbXBsZXMgPSBhdXRvbHlzaXNfc3VtbWFyeSRTYW1wbGVzLAogICAgICAgICAgICAgUl92YWx1ZSA9IGFicyhhdXRvbHlzaXNfc3VtbWFyeSRSKSwgTW9kZWwgPSAiQXV0b2x5c2lzIikKKSkKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJGaWd1cmU1X1NhbXBsZV9TaXplX0FuYWx5c2lzLnBkZiIpLCAKICAgICAgIGZpZ3VyZTVfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOCwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDkuIFRvcCA1IFRpc3N1ZXMgUXVhbGl0eSBPdmVybGF5CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgR2V0IHRvcCA1IHRpc3N1ZXMgZm9yIGVhY2ggc2NvcmUKZ2V0X3RvcDVfdGlzc3VlcyA8LSBmdW5jdGlvbih0YXJnZXRfbmFtZSkgewogIHJfdmFsdWVzIDwtIHNhcHBseShhbGxfcmVzdWx0c1tbdGFyZ2V0X25hbWVdXSwgZnVuY3Rpb24oeCkgewogICAgaWYoaXMubnVsbCh4KSB8fCBpcy5udWxsKHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkgcmV0dXJuKDApCiAgIyByZXR1cm4oc3FydCh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikpICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkgICMgVXNlIGFic29sdXRlIFIgZm9yIHJhbmtpbmcKICB9KQogIAogIHJfdmFsdWVzIDwtIHJfdmFsdWVzW3JfdmFsdWVzID4gMF0KICB0b3A1IDwtIHNvcnQocl92YWx1ZXMsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjVdCiAgCiAgcmV0dXJuKGRhdGEuZnJhbWUoCiAgICB0aXNzdWUgPSBuYW1lcyh0b3A1KSwKICAgIGNvcnJlbGF0aW9uX3IgPSBhcy5udW1lcmljKHRvcDUpCiAgKSkKfQoKdG9wNV9yaW4gPC0gZ2V0X3RvcDVfdGlzc3VlcygiU01SSU4iKQp0b3A1X2F1dG8gPC0gZ2V0X3RvcDVfdGlzc3VlcygiU01BVFNTQ1IiKQoKIyBDcmVhdGUgb3ZlcmxheSBwbG90cwpjcmVhdGVfc2ltcGxlX292ZXJsYXkgPC0gZnVuY3Rpb24odGlzc3VlX25hbWUsIHNjb3JlX3R5cGUsIGNvbG9yX3BhbGV0dGUpIHsKICB0aXNzdWVfZGF0YSA8LSBtZXJnZWRfZ3RleFt0aXNzdWVfbWFpbiA9PSB0aXNzdWVfbmFtZV0KICAKICBpZihucm93KHRpc3N1ZV9kYXRhKSA8IDEwKSB7CiAgICByZXR1cm4oZ2dwbG90KCkgKyB0aGVtZV92b2lkKCkpCiAgfQogIAogICMgR2V0IGZlYXR1cmVzIGFuZCBydW4gVU1BUAogIGZlYXR1cmVfY29scyA8LSBncmVwKCJeZmVhdHVyZV8iLCBuYW1lcyh0aXNzdWVfZGF0YSksIHZhbHVlID0gVFJVRSkKICBmZWF0dXJlX21hdHJpeCA8LSBhcy5tYXRyaXgodGlzc3VlX2RhdGFbLCAuLmZlYXR1cmVfY29sc10pCiAgZmVhdHVyZV9tYXRyaXggPC0gc2NhbGUoZmVhdHVyZV9tYXRyaXhbLCBhcHBseShmZWF0dXJlX21hdHJpeCwgMiwgdmFyKSA+IDFlLTEwXSkKICAKICBzZXQuc2VlZCgxMjMpCiAgdW1hcF9yZXN1bHQgPC0gdW1hcChmZWF0dXJlX21hdHJpeCwgbl9uZWlnaGJvcnMgPSBtaW4oMTUsIG5yb3coZmVhdHVyZV9tYXRyaXgpIC0gMSkpCiAgCiAgcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICBVTUFQMSA9IHVtYXBfcmVzdWx0WywgMV0sCiAgICBVTUFQMiA9IHVtYXBfcmVzdWx0WywgMl0sCiAgICBTY29yZSA9IHRpc3N1ZV9kYXRhW1tzY29yZV90eXBlXV0KICApCiAgCiAgIyBTZXBhcmF0ZSBkYXRhIHdpdGggYW5kIHdpdGhvdXQgc2NvcmVzCiAgZGF0YV93aXRoX3Njb3JlcyA8LSBwbG90X2RhdGFbIWlzLm5hKHBsb3RfZGF0YSRTY29yZSksIF0KICBkYXRhX21pc3Npbmdfc2NvcmVzIDwtIHBsb3RfZGF0YVtpcy5uYShwbG90X2RhdGEkU2NvcmUpLCBdCiAgCiAgIyBHZXQgUiB2YWx1ZQogIHRpc3N1ZV9yZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3Njb3JlX3R5cGVdXVtbdGlzc3VlX25hbWVdXQogIHJfdmFsdWUgPC0gaWYoIWlzLm51bGwodGlzc3VlX3Jlc3VsdCkpIHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IgZWxzZSAwCiMgcl92YWx1ZSA8LSBpZighaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgc3FydCh0aXNzdWVfcmVzdWx0JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikgZWxzZSAwICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogIAogIHAgPC0gZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9taXNzaW5nX3Njb3JlcywgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyKSwgCiAgICAgICAgICAgICAgIGNvbG9yID0gImxpZ2h0Z3JleSIsIGFscGhhID0gMC41LCBzaXplID0gMS41KSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX3dpdGhfc2NvcmVzLCBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gU2NvcmUpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArCiAgICBjb2xvcl9wYWxldHRlICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9IHRpc3N1ZV9uYW1lLAogICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJSID0iLCBzcHJpbnRmKCIlLjNmIiwgcl92YWx1ZSkpLAogICAgICB4ID0gIlVNQVAxIiwgeSA9ICJVTUFQMiIKICAgICkKICAKICByZXR1cm4ocCkKfQoKIyBEZWZpbmUgY29sb3IgcGFsZXR0ZXMKcmluX3BhbGV0dGUgPC0gc2NhbGVfY29sb3JfdmlyaWRpc19jKG5hbWUgPSAiUklOXG5TY29yZSIsIG9wdGlvbiA9ICJ2aXJpZGlzIikKYXV0b19wYWxldHRlIDwtIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhuYW1lID0gIkF1dG9seXNpc1xuU2NvcmUiLCBvcHRpb24gPSAicGxhc21hIikKCiMgQ3JlYXRlIHBsb3RzCnJpbl9wbG90cyA8LSBsYXBwbHkoMTo1LCBmdW5jdGlvbihpKSB7CiAgY3JlYXRlX3NpbXBsZV9vdmVybGF5KHRvcDVfcmluJHRpc3N1ZVtpXSwgIlNNUklOIiwgcmluX3BhbGV0dGUpCn0pCgphdXRvX3Bsb3RzIDwtIGxhcHBseSgxOjUsIGZ1bmN0aW9uKGkpIHsKICBjcmVhdGVfc2ltcGxlX292ZXJsYXkodG9wNV9hdXRvJHRpc3N1ZVtpXSwgIlNNQVRTU0NSIiwgYXV0b19wYWxldHRlKQp9KQoKIyBDb21iaW5lIHBhbmVscwpwYW5lbF9hIDwtIGdyaWQuYXJyYW5nZShncm9icyA9IHJpbl9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkEiLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDAuMDIsIGp1c3QgPSAibGVmdCIpKQoKcGFuZWxfYiA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSBhdXRvX3Bsb3RzLCBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYigiQiIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDIwLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IikpCgpmaW5hbF9wbG90IDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBucm93ID0gMikKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJUb3A1X1Rpc3N1ZXNfUXVhbGl0eV9PdmVybGF5X0FCLnBkZiIpLAogICAgICAgZmluYWxfcGxvdCwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTIsIGRwaSA9IDMwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMC4gRmlndXJlIDY6IENyb3NzLVRpc3N1ZSBGZWF0dXJlIENvbnNpc3RlbmN5IEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmNyZWF0ZV9mZWF0dXJlX2NvbnNpc3RlbmN5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgCiAgZXh0cmFjdF9jb25zaXN0ZW50X2ZlYXR1cmVzIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzLCBtaW5fc2VsZWN0aW9ucyA9IDMpIHsKICAgIHRpc3N1ZV9mZWF0dXJlcyA8LSBsaXN0KCkKICAgIAogICAgZm9yKHRpc3N1ZSBpbiBuYW1lcyh0YXJnZXRfcmVzdWx0cykpIHsKICAgICAgaWYoIWlzLm51bGwodGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzKSkgewogICAgICAgIGZlYXR1cmVzIDwtIG5hbWVzKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICBjb3VudHMgPC0gYXMubnVtZXJpYyh0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMpCiAgICAgICAgc2VsZWN0ZWRfZmVhdHVyZXMgPC0gZmVhdHVyZXNbY291bnRzID49IG1pbl9zZWxlY3Rpb25zXQogICAgICAgIHRpc3N1ZV9mZWF0dXJlc1tbdGlzc3VlXV0gPC0gc2VsZWN0ZWRfZmVhdHVyZXMKICAgICAgfQogICAgfQogICAgCiAgICByZXR1cm4odGlzc3VlX2ZlYXR1cmVzKQogIH0KICAKICByaW5fdGlzc3VlX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2lzdGVudF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNUklOIl1dKQogIGF1dG9fdGlzc3VlX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2lzdGVudF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dKQogIAogIGNyZWF0ZV9jb25zaXN0ZW5jeV9oZWF0bWFwIDwtIGZ1bmN0aW9uKHRpc3N1ZV9mZWF0dXJlcywgdGl0bGUsIGNvbG9yX3NjaGVtZSkgewogICAgYWxsX2ZlYXR1cmVzIDwtIHVuaXF1ZSh1bmxpc3QodGlzc3VlX2ZlYXR1cmVzKSkKICAgIGFsbF90aXNzdWVzIDwtIG5hbWVzKHRpc3N1ZV9mZWF0dXJlcykKICAgIAogICAgc2VsZWN0aW9uX21hdHJpeCA8LSBtYXRyaXgoMCwgbnJvdyA9IGxlbmd0aChhbGxfZmVhdHVyZXMpLCBuY29sID0gbGVuZ3RoKGFsbF90aXNzdWVzKSkKICAgIHJvd25hbWVzKHNlbGVjdGlvbl9tYXRyaXgpIDwtIGFsbF9mZWF0dXJlcwogICAgY29sbmFtZXMoc2VsZWN0aW9uX21hdHJpeCkgPC0gYWxsX3Rpc3N1ZXMKICAgIAogICAgZm9yKHRpc3N1ZSBpbiBhbGxfdGlzc3VlcykgewogICAgICBzZWxlY3RlZCA8LSB0aXNzdWVfZmVhdHVyZXNbW3Rpc3N1ZV1dCiAgICAgIHNlbGVjdGlvbl9tYXRyaXhbc2VsZWN0ZWQsIHRpc3N1ZV0gPC0gMQogICAgfQogICAgCiAgICBjb25zaXN0ZW5jeV9zY29yZXMgPC0gcm93U3VtcyhzZWxlY3Rpb25fbWF0cml4KSAvIG5jb2woc2VsZWN0aW9uX21hdHJpeCkKICAgIHRvcF9mZWF0dXJlcyA8LSBuYW1lcyhzb3J0KGNvbnNpc3RlbmN5X3Njb3JlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjI1XQogICAgcGxvdF9tYXRyaXggPC0gc2VsZWN0aW9uX21hdHJpeFt0b3BfZmVhdHVyZXMsIF0KICAgIAogICAgcGxvdF9kYXRhIDwtIGV4cGFuZC5ncmlkKEZlYXR1cmUgPSByb3duYW1lcyhwbG90X21hdHJpeCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaXNzdWUgPSBjb2xuYW1lcyhwbG90X21hdHJpeCkpCiAgICBwbG90X2RhdGEkU2VsZWN0ZWQgPC0gYXMudmVjdG9yKHBsb3RfbWF0cml4KQogICAgcGxvdF9kYXRhJENvbnNpc3RlbmN5X1Njb3JlIDwtIGNvbnNpc3RlbmN5X3Njb3Jlc1twbG90X2RhdGEkRmVhdHVyZV0KICAgIHBsb3RfZGF0YSRGZWF0dXJlX0lEIDwtIHN1YigiZmVhdHVyZV8iLCAiIiwgcGxvdF9kYXRhJEZlYXR1cmUpCiAgICAKICAgIGhlYXRtYXBfcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFRpc3N1ZSwgeSA9IHJlb3JkZXIoRmVhdHVyZV9JRCwgQ29uc2lzdGVuY3lfU2NvcmUpKSkgKwogICAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBmYWN0b3IoU2VsZWN0ZWQpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4zKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gIndoaXRlIiwgIjEiID0gY29sb3Jfc2NoZW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTZWxlY3RlZCIsIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gdGl0bGUsCiAgICAgICAgeCA9IE5VTEwsCiAgICAgICAgeSA9ICJGZWF0dXJlIElEIgogICAgICApCiAgICAKICAgIHJldHVybihoZWF0bWFwX3Bsb3QpCiAgfQogIAogIHBhbmVsX2EgPC0gY3JlYXRlX2NvbnNpc3RlbmN5X2hlYXRtYXAocmluX3Rpc3N1ZV9mZWF0dXJlcywgIkEuIFJJTiBNb2RlbCBGZWF0dXJlIENvbnNpc3RlbmN5IiwgIiMyRThCNTciKQogIHBhbmVsX2IgPC0gY3JlYXRlX2NvbnNpc3RlbmN5X2hlYXRtYXAoYXV0b190aXNzdWVfZmVhdHVyZXMsICJCLiBBdXRvbHlzaXMgTW9kZWwgRmVhdHVyZSBDb25zaXN0ZW5jeSIsICIjRDI2OTFFIikKICAKICBjYWxjdWxhdGVfc3RhYmlsaXR5X2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbih0aXNzdWVfZmVhdHVyZXMpIHsKICAgIGFsbF9mZWF0dXJlcyA8LSB1bmlxdWUodW5saXN0KHRpc3N1ZV9mZWF0dXJlcykpCiAgICBzdGFiaWxpdHlfc2NvcmVzIDwtIHNhcHBseShhbGxfZmVhdHVyZXMsIGZ1bmN0aW9uKGZlYXR1cmUpIHsKICAgICAgdGlzc3Vlc193aXRoX2ZlYXR1cmUgPC0gc3VtKHNhcHBseSh0aXNzdWVfZmVhdHVyZXMsIGZ1bmN0aW9uKHgpIGZlYXR1cmUgJWluJSB4KSkKICAgICAgcmV0dXJuKHRpc3N1ZXNfd2l0aF9mZWF0dXJlIC8gbGVuZ3RoKHRpc3N1ZV9mZWF0dXJlcykpCiAgICB9KQogICAgcmV0dXJuKHN0YWJpbGl0eV9zY29yZXMpCiAgfQogIAogIHJpbl9zdGFiaWxpdHkgPC0gY2FsY3VsYXRlX3N0YWJpbGl0eV9kaXN0cmlidXRpb24ocmluX3Rpc3N1ZV9mZWF0dXJlcykKICBhdXRvX3N0YWJpbGl0eSA8LSBjYWxjdWxhdGVfc3RhYmlsaXR5X2Rpc3RyaWJ1dGlvbihhdXRvX3Rpc3N1ZV9mZWF0dXJlcykKICAKICBzdGFiaWxpdHlfY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKAogICAgRmVhdHVyZSA9IGMobmFtZXMocmluX3N0YWJpbGl0eSksIG5hbWVzKGF1dG9fc3RhYmlsaXR5KSksCiAgICBTdGFiaWxpdHkgPSBjKHJpbl9zdGFiaWxpdHksIGF1dG9fc3RhYmlsaXR5KSwKICAgIE1vZGVsID0gcmVwKGMoIlJJTiIsICJBdXRvbHlzaXMiKSwgYyhsZW5ndGgocmluX3N0YWJpbGl0eSksIGxlbmd0aChhdXRvX3N0YWJpbGl0eSkpKQogICkKICAKICBwYW5lbF9jIDwtIGdncGxvdChzdGFiaWxpdHlfY29tcGFyaXNvbiwgYWVzKHggPSBTdGFiaWxpdHksIGZpbGwgPSBNb2RlbCkpICsKICAgIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSwgYmlucyA9IDE1LCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgIGdlb21fdmxpbmUoZGF0YSA9IHN0YWJpbGl0eV9jb21wYXJpc29uICU+JSBncm91cF9ieShNb2RlbCkgJT4lIHN1bW1hcmlzZShtZWFuX3N0YWIgPSBtZWFuKFN0YWJpbGl0eSkpLAogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW5fc3RhYiwgY29sb3IgPSBNb2RlbCksIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzJFOEI1NyIsICJBdXRvbHlzaXMiID0gIiNEMjY5MUUiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkMuIEZlYXR1cmUgU3RhYmlsaXR5IERpc3RyaWJ1dGlvbiIsCiAgICAgIHggPSAiU3RhYmlsaXR5IFNjb3JlIChGcmFjdGlvbiBvZiBUaXNzdWVzKSIsCiAgICAgIHkgPSAiTnVtYmVyIG9mIEZlYXR1cmVzIiwKICAgICAgc3VidGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGNyb3NzLXRpc3N1ZSBmZWF0dXJlIGNvbnNpc3RlbmN5IgogICAgKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikKICAgICkKICAKICB0b3BfcGFuZWxzIDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBuY29sID0gMikKICBmaWd1cmU2X2NvbWJpbmVkIDwtIGdyaWQuYXJyYW5nZSh0b3BfcGFuZWxzLCBwYW5lbF9jLCBucm93ID0gMiwgaGVpZ2h0cyA9IGMoMiwgMSkpCiAgCiAgcmV0dXJuKGZpZ3VyZTZfY29tYmluZWQpCn0KCmNhbGN1bGF0ZV9mZWF0dXJlX292ZXJsYXBfc3RhdHMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMpIHsKICBleHRyYWN0X3RvcF9mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbiA9IDUwKSB7CiAgICBmZWF0dXJlX2NvdW50cyA8LSBsaXN0KCkKICAgIGZvcih0aXNzdWUgaW4gbmFtZXModGFyZ2V0X3Jlc3VsdHMpKSB7CiAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICBjb3VudHMgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzCiAgICAgICAgZm9yKGZlYXQgaW4gbmFtZXMoY291bnRzKSkgewogICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX2NvdW50c1tbZmVhdF1dKSkgZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICBmZWF0dXJlX2NvdW50c1tbZmVhdF1dIDwtIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gKyBjb3VudHNbW2ZlYXRdXQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICBzb3J0ZWRfZmVhdHVyZXMgPC0gc29ydCh1bmxpc3QoZmVhdHVyZV9jb3VudHMpLCBkZWNyZWFzaW5nID0gVFJVRSkKICAgIHJldHVybihuYW1lcyhzb3J0ZWRfZmVhdHVyZXMpWzE6bWluKG4sIGxlbmd0aChzb3J0ZWRfZmVhdHVyZXMpKV0pCiAgfQogIAogIHJpbl90b3AgPC0gZXh0cmFjdF90b3BfZmVhdHVyZXMoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICBhdXRvX3RvcCA8LSBleHRyYWN0X3RvcF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dKQogIAogIG92ZXJsYXBfY291bnQgPC0gbGVuZ3RoKGludGVyc2VjdChyaW5fdG9wLCBhdXRvX3RvcCkpCiAgcmluX3VuaXF1ZSA8LSBsZW5ndGgoc2V0ZGlmZihyaW5fdG9wLCBhdXRvX3RvcCkpCiAgYXV0b191bmlxdWUgPC0gbGVuZ3RoKHNldGRpZmYoYXV0b190b3AsIHJpbl90b3ApKQogIAogIHJldHVybihsaXN0KGNvbW1vbiA9IG92ZXJsYXBfY291bnQsIHJpbl91bmlxdWUgPSByaW5fdW5pcXVlLCBhdXRvX3VuaXF1ZSA9IGF1dG9fdW5pcXVlKSkKfQoKZmlndXJlNl9wbG90IDwtIGNyZWF0ZV9mZWF0dXJlX2NvbnNpc3RlbmN5X2FuYWx5c2lzKGFsbF9yZXN1bHRzKQpvdmVybGFwX3N0YXRzIDwtIGNhbGN1bGF0ZV9mZWF0dXJlX292ZXJsYXBfc3RhdHMoYWxsX3Jlc3VsdHMpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlNl9GZWF0dXJlX0NvbnNpc3RlbmN5LnBkZiIpLCAKICAgICAgIGZpZ3VyZTZfcGxvdCwgd2lkdGggPSAxOCwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMS4gRmlndXJlIDc6IE1vZGVsIFJlc2lkdWFsIEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmNyZWF0ZV9jb21wcmVoZW5zaXZlX3Jlc2lkdWFsX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzLCB0b3BfbiA9IDYpIHsKICAKICBpZGVudGlmeV90b3BfdGlzc3VlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbikgewogICAgcGVyZm9ybWFuY2Vfc2NvcmVzIDwtIHNhcHBseSh0YXJnZXRfcmVzdWx0cywgZnVuY3Rpb24oeCkgewogICAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICAgICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkKICAgIH0pCiAgICB0b3BfdGlzc3VlcyA8LSBuYW1lcyhzb3J0KHBlcmZvcm1hbmNlX3Njb3JlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOm5dCiAgICByZXR1cm4odG9wX3Rpc3N1ZXNbIWlzLm5hKHRvcF90aXNzdWVzKV0pCiAgfQogIAogIHRvcF9yaW5fdGlzc3VlcyA8LSBpZGVudGlmeV90b3BfdGlzc3VlcyhhbGxfcmVzdWx0c1tbIlNNUklOIl1dLCB0b3BfbikKICB0b3BfYXV0b190aXNzdWVzIDwtIGlkZW50aWZ5X3RvcF90aXNzdWVzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sIHRvcF9uKQogIAogIGNyZWF0ZV9yZXNpZHVhbF9wYW5lbCA8LSBmdW5jdGlvbih0aXNzdWVzLCB0YXJnZXQsIGNvbG9yLCBwYW5lbF90aXRsZSkgewogICAgcmVzaWR1YWxfcGxvdHMgPC0gbGlzdCgpCiAgICAKICAgIGZvcihpIGluIDE6bWluKDMsIGxlbmd0aCh0aXNzdWVzKSkpIHsKICAgICAgdGlzc3VlX25hbWUgPC0gdGlzc3Vlc1tpXQogICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVfbmFtZV1dCiAgICAgIAogICAgICBpZighaXMubnVsbChyZXN1bHQpICYmICFpcy5udWxsKHJlc3VsdCRhbGxfcHJlZGljdGlvbnMpKSB7CiAgICAgICAgcHJlZGljdGlvbnMgPC0gcmVzdWx0JGFsbF9wcmVkaWN0aW9ucwogICAgICAgIHByZWRpY3Rpb25zJHJlc2lkdWFsIDwtIHByZWRpY3Rpb25zJHByZWRpY3RlZCAtIHByZWRpY3Rpb25zJGFjdHVhbAogICAgICAgIHJfdmFsdWUgPC0gcmVzdWx0JHBlcmZvcm1hbmNlJHBvb2xlZF9yCiAgICAgICAgCiAgICAgICAgcmVzaWR1YWxfcGxvdCA8LSBnZ3Bsb3QocHJlZGljdGlvbnMsIGFlcyh4ID0gYWN0dWFsLCB5ID0gcmVzaWR1YWwpKSArCiAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBjb2xvciA9IGNvbG9yLCBzaXplID0gMikgKwogICAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsKICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gVFJVRSwgY29sb3IgPSAiZGFya2JsdWUiLCBhbHBoYSA9IDAuMykgKwogICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIGxhYnMoCiAgICAgICAgICAgIHRpdGxlID0gcGFzdGUodGlzc3VlX25hbWUsICIoUiA9Iiwgcm91bmQocl92YWx1ZSwgMyksICIpIiksCiAgICAgICAgICAgIHggPSBpZihpID09IDMpIHBhc3RlKCJBY3R1YWwiLCBpZmVsc2UodGFyZ2V0ID09ICJTTVJJTiIsICJSSU4iLCAiQXV0b2x5c2lzIikpIGVsc2UgIiIsCiAgICAgICAgICAgIHkgPSBpZihpID09IDEpICJSZXNpZHVhbCIgZWxzZSAiIgogICAgICAgICAgKSArCiAgICAgICAgICB0aGVtZSgKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIikKICAgICAgICAgICkKICAgICAgICAKICAgICAgICByZXNpZHVhbF9wbG90c1tbaV1dIDwtIHJlc2lkdWFsX3Bsb3QKICAgICAgfQogICAgfQogICAgCiAgICBwYW5lbCA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSByZXNpZHVhbF9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihwYW5lbF90aXRsZSwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkpCiAgICByZXR1cm4ocGFuZWwpCiAgfQogIAogIHBhbmVsX2EgPC0gY3JlYXRlX3Jlc2lkdWFsX3BhbmVsKHRvcF9yaW5fdGlzc3VlcywgIlNNUklOIiwgIiM2NkNEQUEiLCAiQS4gUklOIE1vZGVsIFJlc2lkdWFscyIpCiAgcGFuZWxfYiA8LSBjcmVhdGVfcmVzaWR1YWxfcGFuZWwodG9wX2F1dG9fdGlzc3VlcywgIlNNQVRTU0NSIiwgIiNGQzhENjIiLCAiQi4gQXV0b2x5c2lzIE1vZGVsIFJlc2lkdWFscyIpCiAgCiAgY3JlYXRlX3Jlc2lkdWFsX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbigpIHsKICAgIGFsbF9yZXNpZHVhbHMgPC0gZGF0YS5mcmFtZSgpCiAgICAKICAgIGZvcih0YXJnZXQgaW4gYygiU01SSU4iLCAiU01BVFNTQ1IiKSkgewogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGFsbF9wcmVkaWN0aW9ucykpIHsKICAgICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICAgIHJlc2lkdWFsX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgdGlzc3VlID0gdGlzc3VlLAogICAgICAgICAgICB0YXJnZXQgPSBpZmVsc2UodGFyZ2V0ID09ICJTTVJJTiIsICJSSU4iLCAiQXV0b2x5c2lzIiksCiAgICAgICAgICAgIHJlc2lkdWFsID0gcHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsLAogICAgICAgICAgICBhYnNfcmVzaWR1YWwgPSBhYnMocHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsKQogICAgICAgICAgKQogICAgICAgICAgYWxsX3Jlc2lkdWFscyA8LSByYmluZChhbGxfcmVzaWR1YWxzLCByZXNpZHVhbF9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICBkaXN0cmlidXRpb25fcGxvdCA8LSBnZ3Bsb3QoYWxsX3Jlc2lkdWFscywgYWVzKHggPSBhYnNfcmVzaWR1YWwsIGZpbGwgPSB0YXJnZXQpKSArCiAgICAgIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSwgYmlucyA9IDMwLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV92bGluZShkYXRhID0gYWxsX3Jlc2lkdWFscyAlPiUgZ3JvdXBfYnkodGFyZ2V0KSAlPiUgc3VtbWFyaXNlKG1lYW5fcmVzID0gbWVhbihhYnNfcmVzaWR1YWwpKSwKICAgICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW5fcmVzLCBjb2xvciA9IHRhcmdldCksIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjIpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUklOIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpcyIgPSAiI0ZDOEQ2MiIpKSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiMyRThCNTciLCAiQXV0b2x5c2lzIiA9ICIjRDI2OTFFIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJDLiBEaXN0cmlidXRpb24gb2YgQWJzb2x1dGUgUmVzaWR1YWxzIiwKICAgICAgICB4ID0gIkFic29sdXRlIFJlc2lkdWFsIE1hZ25pdHVkZSIsCiAgICAgICAgeSA9ICJGcmVxdWVuY3kiLAogICAgICAgIGZpbGwgPSAiTW9kZWwgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBwcmVkaWN0aW9uIGVycm9ycyBhY3Jvc3MgYWxsIHRpc3N1ZSBtb2RlbHMiCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybihkaXN0cmlidXRpb25fcGxvdCkKICB9CiAgCiAgY3JlYXRlX3BlcmZvcm1hbmNlX3Jlc2lkdWFsX3JlbGF0aW9uc2hpcCA8LSBmdW5jdGlvbigpIHsKICAgIHBlcmZvcm1hbmNlX3Jlc2lkdWFsX2RhdGEgPC0gZGF0YS5mcmFtZSgpCiAgICAKICAgIGZvcih0YXJnZXQgaW4gYygiU01SSU4iLCAiU01BVFNTQ1IiKSkgewogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGFsbF9wcmVkaWN0aW9ucykpIHsKICAgICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICAgIAogICAgICAgICAgcGVyZl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICAgICAgdGFyZ2V0ID0gaWZlbHNlKHRhcmdldCA9PSAiU01SSU4iLCAiUklOIiwgIkF1dG9seXNpcyIpLAogICAgICAgICAgICByX3ZhbHVlID0gYWJzKHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfciksCiAgICAgICAgICAgIHJtc2UgPSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UsCiAgICAgICAgICAgIG1lYW5fYWJzX3Jlc2lkdWFsID0gbWVhbihhYnMocHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsKSkKICAgICAgICAgICkKICAgICAgICAgIHBlcmZvcm1hbmNlX3Jlc2lkdWFsX2RhdGEgPC0gcmJpbmQocGVyZm9ybWFuY2VfcmVzaWR1YWxfZGF0YSwgcGVyZl9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICByZWxhdGlvbnNoaXBfcGxvdCA8LSBnZ3Bsb3QocGVyZm9ybWFuY2VfcmVzaWR1YWxfZGF0YSwgYWVzKHggPSByX3ZhbHVlLCB5ID0gbWVhbl9hYnNfcmVzaWR1YWwsIGNvbG9yID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX3BvaW50KHNpemUgPSAzLjUsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIHNpemUgPSAxLjIpICsKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkQuIE1vZGVsIFBlcmZvcm1hbmNlIHZzIFJlc2lkdWFsIE1hZ25pdHVkZSIsCiAgICAgICAgeCA9ICJDb3JyZWxhdGlvbiBDb2VmZmljaWVudCB8UnwiLAogICAgICAgIHkgPSAiTWVhbiBBYnNvbHV0ZSBSZXNpZHVhbCIsCiAgICAgICAgY29sb3IgPSAiTW9kZWwgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gcHJlZGljdGlvbiBhY2N1cmFjeSBhbmQgZXJyb3IgbWFnbml0dWRlIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHJlbGF0aW9uc2hpcF9wbG90KQogIH0KICAKICBwYW5lbF9jIDwtIGNyZWF0ZV9yZXNpZHVhbF9kaXN0cmlidXRpb24oKQogIHBhbmVsX2QgPC0gY3JlYXRlX3BlcmZvcm1hbmNlX3Jlc2lkdWFsX3JlbGF0aW9uc2hpcCgpCiAgCiAgYm90dG9tX3BhbmVscyA8LSBncmlkLmFycmFuZ2UocGFuZWxfYywgcGFuZWxfZCwgbmNvbCA9IDIpCiAgZmlndXJlN19jb21iaW5lZCA8LSBncmlkLmFycmFuZ2UocGFuZWxfYSwgcGFuZWxfYiwgYm90dG9tX3BhbmVscywgbnJvdyA9IDMsIGhlaWdodHMgPSBjKDEsIDEsIDEpKQogIAogIHJldHVybihmaWd1cmU3X2NvbWJpbmVkKQp9CgphbmFseXplX3Jlc2lkdWFsX3BhdHRlcm5zIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgcmVzaWR1YWxfc3RhdHMgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yKHRhcmdldCBpbiBjKCJTTVJJTiIsICJTTUFUU1NDUiIpKSB7CiAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgcmVzdWx0IDwtIGFsbF9yZXN1bHRzW1t0YXJnZXRdXVtbdGlzc3VlXV0KICAgICAgaWYoIWlzLm51bGwocmVzdWx0KSAmJiAhaXMubnVsbChyZXN1bHQkYWxsX3ByZWRpY3Rpb25zKSkgewogICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICByZXNpZHVhbHMgPC0gcHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsCiAgICAgICAgCiAgICAgICAgc3RhdHNfcm93IDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICB0aXNzdWUgPSB0aXNzdWUsCiAgICAgICAgICB0YXJnZXQgPSB0YXJnZXQsCiAgICAgICAgICBtZWFuX3Jlc2lkdWFsID0gbWVhbihyZXNpZHVhbHMpLAogICAgICAgICAgc2RfcmVzaWR1YWwgPSBzZChyZXNpZHVhbHMpLAogICAgICAgICAgc2tld25lc3MgPSBlMTA3MTo6c2tld25lc3MocmVzaWR1YWxzKSwKICAgICAgICAgIG5vcm1hbGl0eV9wID0gc2hhcGlyby50ZXN0KHNhbXBsZShyZXNpZHVhbHMsIG1pbig1MDAwLCBsZW5ndGgocmVzaWR1YWxzKSkpKSRwLnZhbHVlCiAgICAgICAgKQogICAgICAgIHJlc2lkdWFsX3N0YXRzIDwtIHJiaW5kKHJlc2lkdWFsX3N0YXRzLCBzdGF0c19yb3cpCiAgICAgIH0KICAgIH0KICB9CiAgCiAgcmV0dXJuKHJlc2lkdWFsX3N0YXRzKQp9CgpmaWd1cmU3X3Bsb3QgPC0gY3JlYXRlX2NvbXByZWhlbnNpdmVfcmVzaWR1YWxfYW5hbHlzaXMoYWxsX3Jlc3VsdHMpCnJlc2lkdWFsX3N0YXRpc3RpY3MgPC0gYW5hbHl6ZV9yZXNpZHVhbF9wYXR0ZXJucyhhbGxfcmVzdWx0cykKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJGaWd1cmU3X1Jlc2lkdWFsX0FuYWx5c2lzLnBkZiIpLCAKICAgICAgIGZpZ3VyZTdfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMS4gRmlndXJlIDg6IEJpb2xvZ2ljYWwgRmVhdHVyZSBJbnRlcnByZXRhdGlvbgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfYmlvbG9naWNhbF9pbnRlcnByZXRhdGlvbl9hbmFseXNpcyA8LSBmdW5jdGlvbihhbGxfcmVzdWx0cywgbWVyZ2VkX2d0ZXgpIHsKICAKICBhbmFseXplX2ZlYXR1cmVfY2F0ZWdvcmllcyA8LSBmdW5jdGlvbigpIHsKICAgIGV4dHJhY3RfaW1wb3J0YW50X2ZlYXR1cmVzIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzLCBuID0gNTApIHsKICAgICAgZmVhdHVyZV9pbXBvcnRhbmNlIDwtIGMoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGZlYXR1cmVzIDwtIG5hbWVzKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICAgIGNvdW50cyA8LSBhcy5udW1lcmljKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICAgIGZlYXR1cmVfaW1wb3J0YW5jZSA8LSBjKGZlYXR1cmVfaW1wb3J0YW5jZSwgcmVwKGZlYXR1cmVzLCBjb3VudHMpKQogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgaW1wb3J0YW5jZV90YWJsZSA8LSBzb3J0KHRhYmxlKGZlYXR1cmVfaW1wb3J0YW5jZSksIGRlY3JlYXNpbmcgPSBUUlVFKQogICAgICByZXR1cm4obmFtZXMoaW1wb3J0YW5jZV90YWJsZSlbMTptaW4obiwgbGVuZ3RoKGltcG9ydGFuY2VfdGFibGUpKV0pCiAgICB9CiAgICAKICAgIHJpbl9pbXBvcnRhbnQgPC0gZXh0cmFjdF9pbXBvcnRhbnRfZmVhdHVyZXMoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICAgIGF1dG9faW1wb3J0YW50IDwtIGV4dHJhY3RfaW1wb3J0YW50X2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0pCiAgICAKICAgIGNhdGVnb3JpemVfZmVhdHVyZXMgPC0gZnVuY3Rpb24oZmVhdHVyZV9uYW1lcykgewogICAgICBmZWF0dXJlX251bWJlcnMgPC0gYXMubnVtZXJpYyhzdWIoImZlYXR1cmVfIiwgIiIsIGZlYXR1cmVfbmFtZXMpKQogICAgICBjYXRlZ29yaWVzIDwtIGNhc2Vfd2hlbigKICAgICAgICBmZWF0dXJlX251bWJlcnMgPD0gMTAyNCB+ICJNZWFuIiwKICAgICAgICBmZWF0dXJlX251bWJlcnMgPD0gMjA0OCB+ICJTdGFuZGFyZCBEZXZpYXRpb24iLAogICAgICAgIGZlYXR1cmVfbnVtYmVycyA8PSAzMDcyIH4gIk1pbmltdW0iLAogICAgICAgIFRSVUUgfiAiTWF4aW11bSIKICAgICAgKQogICAgICByZXR1cm4oY2F0ZWdvcmllcykKICAgIH0KICAgIAogICAgcmluX2NhdGVnb3JpZXMgPC0gY2F0ZWdvcml6ZV9mZWF0dXJlcyhyaW5faW1wb3J0YW50KQogICAgYXV0b19jYXRlZ29yaWVzIDwtIGNhdGVnb3JpemVfZmVhdHVyZXMoYXV0b19pbXBvcnRhbnQpCiAgICAKICAgIGNhdGVnb3J5X3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgKICAgICAgQ2F0ZWdvcnkgPSByZXAoYygiTWVhbiIsICJTdGFuZGFyZCBEZXZpYXRpb24iLCAiTWluaW11bSIsICJNYXhpbXVtIiksIDIpLAogICAgICBDb3VudCA9IGModGFibGUocmluX2NhdGVnb3JpZXMpW2MoIk1lYW4iLCAiU3RhbmRhcmQgRGV2aWF0aW9uIiwgIk1pbmltdW0iLCAiTWF4aW11bSIpXSwKICAgICAgICAgICAgICAgdGFibGUoYXV0b19jYXRlZ29yaWVzKVtjKCJNZWFuIiwgIlN0YW5kYXJkIERldmlhdGlvbiIsICJNaW5pbXVtIiwgIk1heGltdW0iKV0pLAogICAgICBNb2RlbCA9IHJlcChjKCJSSU4iLCAiQXV0b2x5c2lzIiksIGVhY2ggPSA0KQogICAgKQogICAgCiAgICBjYXRlZ29yeV9zdW1tYXJ5JENvdW50W2lzLm5hKGNhdGVnb3J5X3N1bW1hcnkkQ291bnQpXSA8LSAwCiAgICAKICAgIGNhdGVnb3J5X3Bsb3QgPC0gZ2dwbG90KGNhdGVnb3J5X3N1bW1hcnksIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBDb3VudCwgZmlsbCA9IE1vZGVsKSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuODUsIHdpZHRoID0gMC43KSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudCksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjcpLCAKICAgICAgICAgICAgICAgIHZqdXN0ID0gLTAuMywgc2l6ZSA9IDQsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkEuIEZlYXR1cmUgQ2F0ZWdvcnkgRGlzdHJpYnV0aW9uIiwKICAgICAgICB4ID0gIlN0YXRpc3RpY2FsIFByb3BlcnR5IENhdGVnb3J5IiwKICAgICAgICB5ID0gIk51bWJlciBvZiBJbXBvcnRhbnQgRmVhdHVyZXMiLAogICAgICAgIHN1YnRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBrZXkgZmVhdHVyZXMgYWNyb3NzIHN0YXRpc3RpY2FsIG1lYXN1cmVzIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybihjYXRlZ29yeV9wbG90KQogIH0KICAKICBjcmVhdGVfdGlzc3VlX3ZhcmlhYmlsaXR5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgZmVhdHVyZV9jb2xzIDwtIGdyZXAoIl5mZWF0dXJlXyIsIG5hbWVzKG1lcmdlZF9ndGV4KSwgdmFsdWUgPSBUUlVFKQogICAgCiAgICBnZXRfZmVhdHVyZV9pbXBvcnRhbmNlIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzKSB7CiAgICAgIGZlYXR1cmVfc2NvcmVzIDwtIGxpc3QoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGNvdW50cyA8LSB0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMKICAgICAgICAgIGZvcihmZWF0IGluIG5hbWVzKGNvdW50cykpIHsKICAgICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX3Njb3Jlc1tbZmVhdF1dKSkgZmVhdHVyZV9zY29yZXNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICAgIGZlYXR1cmVfc2NvcmVzW1tmZWF0XV0gPC0gZmVhdHVyZV9zY29yZXNbW2ZlYXRdXSArIGNvdW50c1tbZmVhdF1dCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICBzb3J0ZWRfc2NvcmVzIDwtIHNvcnQodW5saXN0KGZlYXR1cmVfc2NvcmVzKSwgZGVjcmVhc2luZyA9IFRSVUUpCiAgICAgIHJldHVybihuYW1lcyhzb3J0ZWRfc2NvcmVzKVsxOm1pbigyNSwgbGVuZ3RoKHNvcnRlZF9zY29yZXMpKV0pCiAgICB9CiAgICAKICAgIGltcG9ydGFudF9mZWF0dXJlcyA8LSBnZXRfZmVhdHVyZV9pbXBvcnRhbmNlKGFsbF9yZXN1bHRzW1siU01SSU4iXV0pCiAgICB2YXJpYWJpbGl0eV9kYXRhIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IoZmVhdHVyZSBpbiBpbXBvcnRhbnRfZmVhdHVyZXMpIHsKICAgICAgaWYoZmVhdHVyZSAlaW4lIGZlYXR1cmVfY29scykgewogICAgICAgIHRpc3N1ZV9zdGF0aXN0aWNzIDwtIG1lcmdlZF9ndGV4WywgLigKICAgICAgICAgIG1lYW5fdmFsdWUgPSBtZWFuKGdldChmZWF0dXJlKSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIHNkX3ZhbHVlID0gc2QoZ2V0KGZlYXR1cmUpLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSwgYnkgPSB0aXNzdWVfbWFpbl0KICAgICAgICAKICAgICAgICBvdmVyYWxsX3ZhcmlhbmNlIDwtIHZhcih0aXNzdWVfc3RhdGlzdGljcyRtZWFuX3ZhbHVlLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgCiAgICAgICAgaW1wb3J0YW5jZV9zY29yZSA8LSBzdW0oc2FwcGx5KGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgIGlmKCFpcy5udWxsKHgkZmVhdHVyZV9jb3VudHMpICYmIGZlYXR1cmUgJWluJSBuYW1lcyh4JGZlYXR1cmVfY291bnRzKSkgewogICAgICAgICAgICByZXR1cm4oeCRmZWF0dXJlX2NvdW50c1tbZmVhdHVyZV1dKQogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuKDApCiAgICAgICAgfSkpCiAgICAgICAgCiAgICAgICAgZmVhdHVyZV9udW0gPC0gYXMubnVtZXJpYyhzdWIoImZlYXR1cmVfIiwgIiIsIGZlYXR1cmUpKQogICAgICAgIGNhdGVnb3J5IDwtIGNhc2Vfd2hlbigKICAgICAgICAgIGZlYXR1cmVfbnVtIDw9IDEwMjQgfiAiTWVhbiIsCiAgICAgICAgICBmZWF0dXJlX251bSA8PSAyMDQ4IH4gIlN0ZCBEZXYiLAogICAgICAgICAgZmVhdHVyZV9udW0gPD0gMzA3MiB+ICJNaW5pbXVtIiwKICAgICAgICAgIFRSVUUgfiAiTWF4aW11bSIKICAgICAgICApCiAgICAgICAgCiAgICAgICAgdmFyaWFiaWxpdHlfZGF0YSA8LSByYmluZCh2YXJpYWJpbGl0eV9kYXRhLCBkYXRhLmZyYW1lKAogICAgICAgICAgZmVhdHVyZSA9IGZlYXR1cmUsCiAgICAgICAgICB0aXNzdWVfdmFyaWFuY2UgPSBvdmVyYWxsX3ZhcmlhbmNlLAogICAgICAgICAgaW1wb3J0YW5jZV9zY29yZSA9IGltcG9ydGFuY2Vfc2NvcmUsCiAgICAgICAgICBjYXRlZ29yeSA9IGNhdGVnb3J5CiAgICAgICAgKSkKICAgICAgfQogICAgfQogICAgCiAgICB2YXJpYWJpbGl0eV9wbG90IDwtIGdncGxvdCh2YXJpYWJpbGl0eV9kYXRhLCBhZXMoeCA9IHRpc3N1ZV92YXJpYW5jZSwgeSA9IGltcG9ydGFuY2Vfc2NvcmUsIGNvbG9yID0gY2F0ZWdvcnkpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIGNvbG9yID0gImRhcmtibHVlIikgKwogICAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgbmFtZSA9ICJGZWF0dXJlXG5DYXRlZ29yeSIpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBGZWF0dXJlIEltcG9ydGFuY2UgdnMgVGlzc3VlIFZhcmlhYmlsaXR5IiwKICAgICAgICB4ID0gIlZhcmlhbmNlIEFjcm9zcyBUaXNzdWUgVHlwZXMiLAogICAgICAgIHkgPSAiSW1wb3J0YW5jZSBTY29yZSAoU2VsZWN0aW9uIEZyZXF1ZW5jeSkiLAogICAgICAgIHN1YnRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRpc3N1ZSBkaXNjcmltaW5hdGlvbiBhbmQgZmVhdHVyZSBpbXBvcnRhbmNlIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybih2YXJpYWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfZmVhdHVyZV9jb3JyZWxhdGlvbl9tYXRyaXggPC0gZnVuY3Rpb24oKSB7CiAgICBleHRyYWN0X2NvbnNlbnN1c19mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbiA9IDIwKSB7CiAgICAgIGZlYXR1cmVfY291bnRzIDwtIGxpc3QoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGNvdW50cyA8LSB0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMKICAgICAgICAgIGZvcihmZWF0IGluIG5hbWVzKGNvdW50cykpIHsKICAgICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX2NvdW50c1tbZmVhdF1dKSkgZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICAgIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gPC0gZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSArIGNvdW50c1tbZmVhdF1dCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICBzb3J0ZWRfZmVhdHVyZXMgPC0gc29ydCh1bmxpc3QoZmVhdHVyZV9jb3VudHMpLCBkZWNyZWFzaW5nID0gVFJVRSkKICAgICAgcmV0dXJuKG5hbWVzKHNvcnRlZF9mZWF0dXJlcylbMTptaW4obiwgbGVuZ3RoKHNvcnRlZF9mZWF0dXJlcykpXSkKICAgIH0KICAgIAogICAgY29uc2Vuc3VzX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2Vuc3VzX2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIDE1KQogICAgCiAgICBpZihsZW5ndGgoY29uc2Vuc3VzX2ZlYXR1cmVzKSA+IDEpIHsKICAgICAgZmVhdHVyZV9tYXRyaXggPC0gYXMubWF0cml4KG1lcmdlZF9ndGV4WywgLi5jb25zZW5zdXNfZmVhdHVyZXNdKQogICAgICBmZWF0dXJlX21hdHJpeCA8LSBmZWF0dXJlX21hdHJpeFtjb21wbGV0ZS5jYXNlcyhmZWF0dXJlX21hdHJpeCksIF0KICAgICAgCiAgICAgIGlmKG5yb3coZmVhdHVyZV9tYXRyaXgpID4gMTApIHsKICAgICAgICBjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKGZlYXR1cmVfbWF0cml4LCB1c2UgPSAiY29tcGxldGUub2JzIikKICAgICAgICAKICAgICAgICBjb3JfbG9uZyA8LSByZXNoYXBlMjo6bWVsdChjb3JyZWxhdGlvbl9tYXRyaXgpCiAgICAgICAgY29yX2xvbmckVmFyMSA8LSBzdWIoImZlYXR1cmVfIiwgIiIsIGNvcl9sb25nJFZhcjEpCiAgICAgICAgY29yX2xvbmckVmFyMiA8LSBzdWIoImZlYXR1cmVfIiwgIiIsIGNvcl9sb25nJFZhcjIpCiAgICAgICAgCiAgICAgICAgY29ycmVsYXRpb25fcGxvdCA8LSBnZ3Bsb3QoY29yX2xvbmcsIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsKICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjUpICsKICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICIjMjE2NkFDIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICIjRDczMDI3IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gMCwgbmFtZSA9ICJDb3JyZWxhdGlvblxuQ29lZmZpY2llbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKC0xLCAxKSkgKwogICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIHRoZW1lKAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiCiAgICAgICAgICApICsKICAgICAgICAgIGxhYnMoCiAgICAgICAgICAgIHRpdGxlID0gIkMuIEZlYXR1cmUgQ29ycmVsYXRpb24gTWF0cml4IiwKICAgICAgICAgICAgeCA9ICJGZWF0dXJlIElEIiwKICAgICAgICAgICAgeSA9ICJGZWF0dXJlIElEIiwKICAgICAgICAgICAgc3VidGl0bGUgPSAiQ29ycmVsYXRpb24gc3RydWN0dXJlIGFtb25nIHRvcCBwcmVkaWN0aXZlIGZlYXR1cmVzIgogICAgICAgICAgKSArCiAgICAgICAgICBjb29yZF9maXhlZCgpCiAgICAgICAgCiAgICAgICAgcmV0dXJuKGNvcnJlbGF0aW9uX3Bsb3QpCiAgICAgIH0KICAgIH0KICAgIAogICAgZW1wdHlfcGxvdCA8LSBnZ3Bsb3QoKSArIAogICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjUsIHkgPSAwLjUsIAogICAgICAgICAgICAgICBsYWJlbCA9ICJJbnN1ZmZpY2llbnQgZGF0YVxuZm9yIGNvcnJlbGF0aW9uIGFuYWx5c2lzIiwgCiAgICAgICAgICAgICAgIHNpemUgPSA2LCBoanVzdCA9IDAuNSkgKwogICAgICB0aGVtZV92b2lkKCkgKwogICAgICBsYWJzKHRpdGxlID0gIkMuIEZlYXR1cmUgQ29ycmVsYXRpb24gTWF0cml4IikgKwogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpKQogICAgCiAgICByZXR1cm4oZW1wdHlfcGxvdCkKICB9CiAgCiAgcGFuZWxfYSA8LSBhbmFseXplX2ZlYXR1cmVfY2F0ZWdvcmllcygpCiAgcGFuZWxfYiA8LSBjcmVhdGVfdGlzc3VlX3ZhcmlhYmlsaXR5X2FuYWx5c2lzKCkKICBwYW5lbF9jIDwtIGNyZWF0ZV9mZWF0dXJlX2NvcnJlbGF0aW9uX21hdHJpeCgpCiAgCiAgdG9wX3BhbmVscyA8LSBncmlkLmFycmFuZ2UocGFuZWxfYSwgcGFuZWxfYiwgbmNvbCA9IDIpCiAgZmlndXJlOF9jb21iaW5lZCA8LSBncmlkLmFycmFuZ2UodG9wX3BhbmVscywgcGFuZWxfYywgbnJvdyA9IDIsIGhlaWdodHMgPSBjKDEsIDEpKQogIAogIHJldHVybihmaWd1cmU4X2NvbWJpbmVkKQp9CgphbmFseXplX2Jpb2xvZ2ljYWxfc2lnbmlmaWNhbmNlIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgZm9yKHRhcmdldCBpbiBjKCJTTVJJTiIsICJTTUFUU1NDUiIpKSB7CiAgICBhbGxfZmVhdHVyZXMgPC0gdW5saXN0KGxhcHBseShhbGxfcmVzdWx0c1tbdGFyZ2V0XV0sIGZ1bmN0aW9uKHgpIHsKICAgICAgaWYoIWlzLm51bGwoeCRmZWF0dXJlX2NvdW50cykpIG5hbWVzKHgkZmVhdHVyZV9jb3VudHMpIGVsc2UgTlVMTAogICAgfSkpCiAgICAKICAgIGZlYXR1cmVfbnVtcyA8LSBhcy5udW1lcmljKHN1YigiZmVhdHVyZV8iLCAiIiwgYWxsX2ZlYXR1cmVzKSkKICAgIG1lYW5fZmVhdHVyZXMgPC0gc3VtKGZlYXR1cmVfbnVtcyA8PSAxMDI0KQogICAgc3RkX2ZlYXR1cmVzIDwtIHN1bShmZWF0dXJlX251bXMgPiAxMDI0ICYgZmVhdHVyZV9udW1zIDw9IDIwNDgpCiAgICBtaW5fZmVhdHVyZXMgPC0gc3VtKGZlYXR1cmVfbnVtcyA+IDIwNDggJiBmZWF0dXJlX251bXMgPD0gMzA3MikKICAgIG1heF9mZWF0dXJlcyA8LSBzdW0oZmVhdHVyZV9udW1zID4gMzA3MikKICB9Cn0KCmZpZ3VyZThfcGxvdCA8LSBjcmVhdGVfYmlvbG9naWNhbF9pbnRlcnByZXRhdGlvbl9hbmFseXNpcyhhbGxfcmVzdWx0cywgbWVyZ2VkX2d0ZXgpCmFuYWx5emVfYmlvbG9naWNhbF9zaWduaWZpY2FuY2UoYWxsX3Jlc3VsdHMpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlOF9CaW9sb2dpY2FsX0ludGVycHJldGF0aW9uLnBkZiIpLCAKICAgICAgIGZpZ3VyZThfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMi4gU3VwcGxlbWVudGFyeSBGaWd1cmUgUzE6IFF1YWxpdHkgU2NvcmUgRGlzdHJpYnV0aW9ucyBieSBUaXNzdWUKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KY3JlYXRlX3F1YWxpdHlfc2NvcmVfZGlzdHJpYnV0aW9ucyA8LSBmdW5jdGlvbihtZXJnZWRfZ3RleCkgewogIAogIHByZXBhcmVfcXVhbGl0eV9kYXRhIDwtIGZ1bmN0aW9uKCkgewogICAgcmluX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNUklOKSwgLih0aXNzdWVfbWFpbiwgU01SSU4pXQogICAgc2V0bmFtZXMocmluX2RhdGEsICJTTVJJTiIsICJTY29yZSIpCiAgICByaW5fZGF0YSRTY29yZV9UeXBlIDwtICJSSU4gU2NvcmUiCiAgICAKICAgIGF1dG9fZGF0YSA8LSBtZXJnZWRfZ3RleFshaXMubmEoU01BVFNTQ1IpLCAuKHRpc3N1ZV9tYWluLCBTTUFUU1NDUildCiAgICBzZXRuYW1lcyhhdXRvX2RhdGEsICJTTUFUU1NDUiIsICJTY29yZSIpCiAgICBhdXRvX2RhdGEkU2NvcmVfVHlwZSA8LSAiQXV0b2x5c2lzIFNjb3JlIgogICAgCiAgICByZXR1cm4obGlzdChyaW4gPSByaW5fZGF0YSwgYXV0b2x5c2lzID0gYXV0b19kYXRhKSkKICB9CiAgCiAgZ2V0X3RvcF90aXNzdWVzIDwtIGZ1bmN0aW9uKG5fdGlzc3VlcyA9IDIwKSB7CiAgICB0aXNzdWVfY291bnRzIDwtIG1lcmdlZF9ndGV4WywgLk4sIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKC1OKV0KICAgIHJldHVybihoZWFkKHRpc3N1ZV9jb3VudHMkdGlzc3VlX21haW4sIG5fdGlzc3VlcykpCiAgfQogIAogIHF1YWxpdHlfZGF0YSA8LSBwcmVwYXJlX3F1YWxpdHlfZGF0YSgpCiAgdG9wX3Rpc3N1ZXMgPC0gZ2V0X3RvcF90aXNzdWVzKCkKICAKICByaW5fZmlsdGVyZWQgPC0gcXVhbGl0eV9kYXRhJHJpblt0aXNzdWVfbWFpbiAlaW4lIHRvcF90aXNzdWVzXQogIGF1dG9fZmlsdGVyZWQgPC0gcXVhbGl0eV9kYXRhJGF1dG9seXNpc1t0aXNzdWVfbWFpbiAlaW4lIHRvcF90aXNzdWVzXQogIAogIHJpbl9maWx0ZXJlZCR0aXNzdWVfbWFpbiA8LSBmYWN0b3IocmluX2ZpbHRlcmVkJHRpc3N1ZV9tYWluLCBsZXZlbHMgPSB0b3BfdGlzc3VlcykKICBhdXRvX2ZpbHRlcmVkJHRpc3N1ZV9tYWluIDwtIGZhY3RvcihhdXRvX2ZpbHRlcmVkJHRpc3N1ZV9tYWluLCBsZXZlbHMgPSB0b3BfdGlzc3VlcykKICAKICBjcmVhdGVfcmluX2Rpc3RyaWJ1dGlvbl9wYW5lbCA8LSBmdW5jdGlvbigpIHsKICAgIHJpbl92aW9saW4gPC0gZ2dwbG90KHJpbl9maWx0ZXJlZCwgYWVzKHggPSB0aXNzdWVfbWFpbiwgeSA9IFNjb3JlKSkgKwogICAgICBnZW9tX3Zpb2xpbihmaWxsID0gIiM2NkNEQUEiLCBhbHBoYSA9IDAuOCwgdHJpbSA9IEZBTFNFLCBzY2FsZSA9ICJ3aWR0aCIpICsKICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xNSwgYWxwaGEgPSAwLjksIG91dGxpZXIuc2l6ZSA9IDEsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lZGlhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDEuNSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQS4gUklOIFNjb3JlIERpc3RyaWJ1dGlvbnMgYnkgVGlzc3VlIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIlJOQSBJbnRlZ3JpdHkgTnVtYmVyIGFjcm9zcyBtYWpvciB0aXNzdWUgdHlwZXMiLAogICAgICAgIHggPSBOVUxMLAogICAgICAgIHkgPSAiUklOIFNjb3JlIgogICAgICApICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwgMTApLCBicmVha3MgPSBzZXEoMiwgMTAsIDIpKQogICAgCiAgICByZXR1cm4ocmluX3Zpb2xpbikKICB9CiAgCiAgY3JlYXRlX2F1dG9seXNpc19kaXN0cmlidXRpb25fcGFuZWwgPC0gZnVuY3Rpb24oKSB7CiAgICBhdXRvX3Zpb2xpbiA8LSBnZ3Bsb3QoYXV0b19maWx0ZXJlZCwgYWVzKHggPSB0aXNzdWVfbWFpbiwgeSA9IFNjb3JlKSkgKwogICAgICBnZW9tX3Zpb2xpbihmaWxsID0gIiNGQzhENjIiLCBhbHBoYSA9IDAuOCwgdHJpbSA9IEZBTFNFLCBzY2FsZSA9ICJ3aWR0aCIpICsKICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xNSwgYWxwaGEgPSAwLjksIG91dGxpZXIuc2l6ZSA9IDEsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lZGlhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtyZWQiLCBzaXplID0gMS41KSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBBdXRvbHlzaXMgU2NvcmUgRGlzdHJpYnV0aW9ucyBieSBUaXNzdWUgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiVGlzc3VlIGRlZ3JhZGF0aW9uIGxldmVscyBhY3Jvc3MgbWFqb3IgdGlzc3VlIHR5cGVzIiwKICAgICAgICB4ID0gIlRpc3N1ZSBUeXBlIiwKICAgICAgICB5ID0gIkF1dG9seXNpcyBTY29yZSIKICAgICAgKSArCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMpLCBicmVha3MgPSAwOjMsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAgKE5vbmUpIiwgIjEgKE1pbGQpIiwgIjIgKE1vZGVyYXRlKSIsICIzIChTZXZlcmUpIikpCiAgICAKICAgIHJldHVybihhdXRvX3Zpb2xpbikKICB9CiAgCiAgY2FsY3VsYXRlX2Rpc3RyaWJ1dGlvbl9zdGF0cyA8LSBmdW5jdGlvbigpIHsKICAgIHJpbl9zdGF0cyA8LSByaW5fZmlsdGVyZWRbLCAuKAogICAgICBtZWFuX3Njb3JlID0gcm91bmQobWVhbihTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIG1lZGlhbl9zY29yZSA9IHJvdW5kKG1lZGlhbihTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIHNkX3Njb3JlID0gcm91bmQoc2QoU2NvcmUsIG5hLnJtID0gVFJVRSksIDIpLAogICAgICBzYW1wbGVfc2l6ZSA9IC5OCiAgICApLCBieSA9IHRpc3N1ZV9tYWluXVtvcmRlcigtbWVhbl9zY29yZSldCiAgICAKICAgIGF1dG9fc3RhdHMgPC0gYXV0b19maWx0ZXJlZFssIC4oCiAgICAgIG1lYW5fc2NvcmUgPSByb3VuZChtZWFuKFNjb3JlLCBuYS5ybSA9IFRSVUUpLCAyKSwKICAgICAgbWVkaWFuX3Njb3JlID0gcm91bmQobWVkaWFuKFNjb3JlLCBuYS5ybSA9IFRSVUUpLCAyKSwKICAgICAgc2Rfc2NvcmUgPSByb3VuZChzZChTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIHNhbXBsZV9zaXplID0gLk4KICAgICksIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKG1lYW5fc2NvcmUpXQogICAgCiAgICByZXR1cm4obGlzdChyaW5fc3RhdHMgPSByaW5fc3RhdHMsIGF1dG9fc3RhdHMgPSBhdXRvX3N0YXRzKSkKICB9CiAgCiAgcGFuZWxfYSA8LSBjcmVhdGVfcmluX2Rpc3RyaWJ1dGlvbl9wYW5lbCgpCiAgcGFuZWxfYiA8LSBjcmVhdGVfYXV0b2x5c2lzX2Rpc3RyaWJ1dGlvbl9wYW5lbCgpCiAgZGlzdHJpYnV0aW9uX3N0YXRzIDwtIGNhbGN1bGF0ZV9kaXN0cmlidXRpb25fc3RhdHMoKQogIAogIHN1cHBfZmlnX3MxIDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBucm93ID0gMikKICAKICByZXR1cm4oc3VwcF9maWdfczEpCn0KCmFuYWx5emVfcXVhbGl0eV9jb3JyZWxhdGlvbnMgPC0gZnVuY3Rpb24obWVyZ2VkX2d0ZXgpIHsKICBxdWFsaXR5X3N1YnNldCA8LSBtZXJnZWRfZ3RleFshaXMubmEoU01SSU4pICYgIWlzLm5hKFNNQVRTU0NSKV0KICAKICBpZihucm93KHF1YWxpdHlfc3Vic2V0KSA+IDEwKSB7CiAgICBjb3JyZWxhdGlvbl90ZXN0IDwtIGNvci50ZXN0KHF1YWxpdHlfc3Vic2V0JFNNUklOLCBxdWFsaXR5X3N1YnNldCRTTUFUU1NDUikKICAgIHJldHVybihjb3JyZWxhdGlvbl90ZXN0KQogIH0gZWxzZSB7CiAgICByZXR1cm4oTlVMTCkKICB9Cn0KCnN1cHBfZmlndXJlX3MxIDwtIGNyZWF0ZV9xdWFsaXR5X3Njb3JlX2Rpc3RyaWJ1dGlvbnMobWVyZ2VkX2d0ZXgpCnF1YWxpdHlfY29ycmVsYXRpb25zIDwtIGFuYWx5emVfcXVhbGl0eV9jb3JyZWxhdGlvbnMobWVyZ2VkX2d0ZXgpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiU3VwcEZpZ19TMV9RdWFsaXR5X0Rpc3RyaWJ1dGlvbnMucGRmIiksIAogICAgICAgc3VwcF9maWd1cmVfczEsIHdpZHRoID0gMTgsIGhlaWdodCA9IDEyLCBkcGkgPSA2MDAsIGJnID0gIndoaXRlIikKYGBgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTMuIFN1cHBsZW1lbnRhcnkgRmlndXJlIFMyOiBDcm9zcy12YWxpZGF0aW9uIFN0YWJpbGl0eSBBbmFseXNpcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfY3Zfc3RhYmlsaXR5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgCiAgZXh0cmFjdF9mb2xkX3BlcmZvcm1hbmNlIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzKSB7CiAgICBmb2xkX3BlcmZvcm1hbmNlIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICByZXN1bHQgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dCiAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGZvbGRfcmVzdWx0cykpIHsKICAgICAgICAKICAgICAgICBmb3IoZm9sZF9yZXN1bHQgaW4gcmVzdWx0JGZvbGRfcmVzdWx0cykgewogICAgICAgICAgZm9sZF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICAgICAgZm9sZCA9IGZvbGRfcmVzdWx0JGZvbGQsCiAgICAgICAgICAgIHJfdmFsdWUgPSBhYnMoZm9sZF9yZXN1bHQkciksCiAgICAgICAgICAgIHJtc2UgPSBmb2xkX3Jlc3VsdCRybXNlLAogICAgICAgICAgICBtYWUgPSBmb2xkX3Jlc3VsdCRtYWUKICAgICAgICAgICkKICAgICAgICAgIGZvbGRfcGVyZm9ybWFuY2UgPC0gcmJpbmQoZm9sZF9wZXJmb3JtYW5jZSwgZm9sZF9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICByZXR1cm4oZm9sZF9wZXJmb3JtYW5jZSkKICB9CiAgCiAgcmluX2ZvbGRfZGF0YSA8LSBleHRyYWN0X2ZvbGRfcGVyZm9ybWFuY2UoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICBhdXRvX2ZvbGRfZGF0YSA8LSBleHRyYWN0X2ZvbGRfcGVyZm9ybWFuY2UoYWxsX3Jlc3VsdHNbWyJTTUFUU1NDUiJdXSkKICAKICByaW5fZm9sZF9kYXRhJHRhcmdldCA8LSAiUklOIgogIGF1dG9fZm9sZF9kYXRhJHRhcmdldCA8LSAiQXV0b2x5c2lzIgogIGNvbWJpbmVkX2ZvbGRfZGF0YSA8LSByYmluZChyaW5fZm9sZF9kYXRhLCBhdXRvX2ZvbGRfZGF0YSkKICAKICBjcmVhdGVfc3RhYmlsaXR5X3NjYXR0ZXIgPC0gZnVuY3Rpb24oKSB7CiAgICBzdGFiaWxpdHlfbWV0cmljcyA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lCiAgICAgIGdyb3VwX2J5KHRpc3N1ZSwgdGFyZ2V0KSAlPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBzZF9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBjdl9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICAgICkgJT4lCiAgICAgIGZpbHRlcihpcy5maW5pdGUoY3ZfcikgJiBjdl9yID4gMCkKICAgIAogICAgc3RhYmlsaXR5X3Bsb3QgPC0gZ2dwbG90KHN0YWJpbGl0eV9tZXRyaWNzLCBhZXMoeCA9IG1lYW5fciwgeSA9IGN2X3IsIGNvbG9yID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX3BvaW50KHNpemUgPSAzLjUsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMpICsKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkEuIE1vZGVsIFN0YWJpbGl0eSB2cyBQZXJmb3JtYW5jZSIsCiAgICAgICAgeCA9ICJNZWFuIHxSfCBBY3Jvc3MgRm9sZHMiLAogICAgICAgIHkgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uICh8UnwpIiwKICAgICAgICBjb2xvciA9ICJNb2RlbCBUeXBlIiwKICAgICAgICBzdWJ0aXRsZSA9ICJMb3dlciBDViBpbmRpY2F0ZXMgbW9yZSBzdGFibGUgY3Jvc3MtdmFsaWRhdGlvbiBwZXJmb3JtYW5jZSIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHN0YWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfZm9sZF92YXJpYWJpbGl0eV9ib3hwbG90cyA8LSBmdW5jdGlvbigpIHsKICAgIHRvcF90aXNzdWVzX3JpbiA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lIAogICAgICBmaWx0ZXIodGFyZ2V0ID09ICJSSU4iKSAlPiUKICAgICAgZ3JvdXBfYnkodGlzc3VlKSAlPiUKICAgICAgc3VtbWFyaXNlKG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgICAgIGFycmFuZ2UoZGVzYyhtZWFuX3IpKSAlPiUgCiAgICAgIHNsaWNlKDE6OCkgJT4lIAogICAgICBwdWxsKHRpc3N1ZSkKICAgIAogICAgdG9wX3Rpc3N1ZXNfYXV0byA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lIAogICAgICBmaWx0ZXIodGFyZ2V0ID09ICJBdXRvbHlzaXMiKSAlPiUKICAgICAgZ3JvdXBfYnkodGlzc3VlKSAlPiUKICAgICAgc3VtbWFyaXNlKG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgICAgIGFycmFuZ2UoZGVzYyhtZWFuX3IpKSAlPiUgCiAgICAgIHNsaWNlKDE6OCkgJT4lIAogICAgICBwdWxsKHRpc3N1ZSkKICAgIAogICAgZm9sZF9zdWJzZXQgPC0gY29tYmluZWRfZm9sZF9kYXRhICU+JQogICAgICBmaWx0ZXIoKHRhcmdldCA9PSAiUklOIiAmIHRpc3N1ZSAlaW4lIHRvcF90aXNzdWVzX3JpbikgfAogICAgICAgICAgICAgKHRhcmdldCA9PSAiQXV0b2x5c2lzIiAmIHRpc3N1ZSAlaW4lIHRvcF90aXNzdWVzX2F1dG8pKQogICAgCiAgICB2YXJpYWJpbGl0eV9wbG90IDwtIGdncGxvdChmb2xkX3N1YnNldCwgYWVzKHggPSB0aXNzdWUsIHkgPSByX3ZhbHVlLCBmaWxsID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjgsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgZmFjZXRfd3JhcCh+dGFyZ2V0LCBzY2FsZXMgPSAiZnJlZV94IiwgbmNvbCA9IDEpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUklOIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpcyIgPSAiI0ZDOEQ2MiIpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQi4gQ3Jvc3MtRm9sZCBQZXJmb3JtYW5jZSBWYXJpYWJpbGl0eSIsCiAgICAgICAgeCA9ICJUaXNzdWUgVHlwZSIsCiAgICAgICAgeSA9ICJ8UnwgVmFsdWUgQWNyb3NzIEZvbGRzIiwKICAgICAgICBzdWJ0aXRsZSA9ICJCb3ggcGxvdHMgc2hvdyBkaXN0cmlidXRpb24gb2YgcGVyZm9ybWFuY2UgYWNyb3NzIENWIGZvbGRzIgogICAgICApCiAgICAKICAgIHJldHVybih2YXJpYWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfc3RhYmlsaXR5X3JhbmtpbmcgPC0gZnVuY3Rpb24oKSB7CiAgICBzdGFiaWxpdHlfcmFua2luZyA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lCiAgICAgIGdyb3VwX2J5KHRpc3N1ZSwgdGFyZ2V0KSAlPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBjdl9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICAgICkgJT4lCiAgICAgIGZpbHRlcihpcy5maW5pdGUoY3ZfcikgJiBjdl9yID4gMCkgJT4lCiAgICAgIGFycmFuZ2UoY3ZfcikgJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBzdGFiaWxpdHlfcmFuayA9IHJvd19udW1iZXIoKSwKICAgICAgICBzdGFiaWxpdHlfY2F0ZWdvcnkgPSBjdXQoY3ZfciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDAuMSwgMC4yLCAwLjUsIEluZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IFN0YWJsZSIsICJTdGFibGUiLCAiTW9kZXJhdGUiLCAiVW5zdGFibGUiKSkKICAgICAgKQogICAgCiAgICByYW5raW5nX3Bsb3QgPC0gZ2dwbG90KHN0YWJpbGl0eV9yYW5raW5nLCBhZXMoeCA9IHN0YWJpbGl0eV9yYW5rLCB5ID0gY3ZfciwgY29sb3IgPSB0YXJnZXQpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoMC4xLCAwLjIsIDAuNSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGFscGhhID0gMC42KSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJDLiBNb2RlbCBTdGFiaWxpdHkgUmFua2luZyIsCiAgICAgICAgeCA9ICJTdGFiaWxpdHkgUmFuayAoMSA9IE1vc3QgU3RhYmxlKSIsCiAgICAgICAgeSA9ICJDb2VmZmljaWVudCBvZiBWYXJpYXRpb24gKHxSfCkiLAogICAgICAgIGNvbG9yID0gIk1vZGVsIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIkhvcml6b250YWwgbGluZXMgaW5kaWNhdGUgc3RhYmlsaXR5IHRocmVzaG9sZHMiCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKQogICAgCiAgICByZXR1cm4ocmFua2luZ19wbG90KQogIH0KICAKICBwYW5lbF9hIDwtIGNyZWF0ZV9zdGFiaWxpdHlfc2NhdHRlcigpCiAgcGFuZWxfYiA8LSBjcmVhdGVfZm9sZF92YXJpYWJpbGl0eV9ib3hwbG90cygpCiAgcGFuZWxfYyA8LSBjcmVhdGVfc3RhYmlsaXR5X3JhbmtpbmcoKQogIAogIHRvcF9wYW5lbHMgPC0gZ3JpZC5hcnJhbmdlKHBhbmVsX2EsIHBhbmVsX2MsIG5jb2wgPSAyKQogIHN1cHBfZmlnX3MyIDwtIGdyaWQuYXJyYW5nZSh0b3BfcGFuZWxzLCBwYW5lbF9iLCBucm93ID0gMiwgaGVpZ2h0cyA9IGMoMSwgMS41KSkKICAKICByZXR1cm4oc3VwcF9maWdfczIpCn0KCmNhbGN1bGF0ZV9zdGFiaWxpdHlfc3RhdHMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMpIHsKICBzdGFiaWxpdHlfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKCkKICAKICBmb3IodGFyZ2V0IGluIGMoIlNNUklOIiwgIlNNQVRTU0NSIikpIHsKICAgIGZvcih0aXNzdWUgaW4gbmFtZXMoYWxsX3Jlc3VsdHNbW3RhcmdldF1dKSkgewogICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICBpZighaXMubnVsbChyZXN1bHQpICYmICFpcy5udWxsKHJlc3VsdCRmb2xkX3Jlc3VsdHMpKSB7CiAgICAgICAgCiAgICAgICAgZm9sZF9yX3ZhbHVlcyA8LSBzYXBwbHkocmVzdWx0JGZvbGRfcmVzdWx0cywgZnVuY3Rpb24oeCkgYWJzKHgkcikpCiAgICAgICAgCiAgICAgICAgc3RhYmlsaXR5X3JvdyA8LSBkYXRhLmZyYW1lKAogICAgICAgICAgdGlzc3VlID0gdGlzc3VlLAogICAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0LAogICAgICAgICAgbWVhbl9yID0gbWVhbihmb2xkX3JfdmFsdWVzKSwKICAgICAgICAgIGN2X3IgPSBzZChmb2xkX3JfdmFsdWVzKSAvIG1lYW4oZm9sZF9yX3ZhbHVlcyksCiAgICAgICAgICBtaW5fciA9IG1pbihmb2xkX3JfdmFsdWVzKSwKICAgICAgICAgIG1heF9yID0gbWF4KGZvbGRfcl92YWx1ZXMpCiAgICAgICAgKQogICAgICAgIHN0YWJpbGl0eV9zdW1tYXJ5IDwtIHJiaW5kKHN0YWJpbGl0eV9zdW1tYXJ5LCBzdGFiaWxpdHlfcm93KQogICAgICB9CiAgICB9CiAgfQogIAogIGJ5X3RhcmdldCA8LSBzdGFiaWxpdHlfc3VtbWFyeSAlPiUKICAgIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIG1lYW5fY3YgPSByb3VuZChtZWFuKGN2X3IsIG5hLnJtID0gVFJVRSksIDMpLAogICAgICBtZWRpYW5fY3YgPSByb3VuZChtZWRpYW4oY3ZfciwgbmEucm0gPSBUUlVFKSwgMyksCiAgICAgIHN0YWJsZV9tb2RlbHMgPSBzdW0oY3ZfciA8IDAuMiwgbmEucm0gPSBUUlVFKSwKICAgICAgdG90YWxfbW9kZWxzID0gbigpLAogICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICApCiAgCiAgcmV0dXJuKHN0YWJpbGl0eV9zdW1tYXJ5KQp9CgpzdXBwX2ZpZ3VyZV9zMiA8LSBjcmVhdGVfY3Zfc3RhYmlsaXR5X2FuYWx5c2lzKGFsbF9yZXN1bHRzKQpzdGFiaWxpdHlfc3RhdHMgPC0gY2FsY3VsYXRlX3N0YWJpbGl0eV9zdGF0cyhhbGxfcmVzdWx0cykKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJTdXBwRmlnX1MyX0NWX1N0YWJpbGl0eS5wZGYiKSwgCiAgICAgICBzdXBwX2ZpZ3VyZV9zMiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTIsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTQuIFN1cHBsZW1lbnRhcnkgRmlndXJlIFMzOiBFeHRlbmRlZCBDb21wYXJhdGl2ZSBBbmFseXNpcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfZXh0ZW5kZWRfY29tcGFyYXRpdmVfYW5hbHlzaXMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkgewogIAogIGNyZWF0ZV9jcm9zc19tb2RlbF9jb3JyZWxhdGlvbiA8LSBmdW5jdGlvbigpIHsKICAgIGNvbW1vbl90aXNzdWVzIDwtIGludGVyc2VjdChyaW5fc3VtbWFyeSRUaXNzdWUsIGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSkKICAgIAogICAgY29tcGFyaXNvbl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgIFRpc3N1ZSA9IGNvbW1vbl90aXNzdWVzLAogICAgICBSSU5fUiA9IHJpbl9zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIHJpbl9zdW1tYXJ5JFRpc3N1ZSldLAogICAgICBBdXRvX1IgPSBhdXRvbHlzaXNfc3VtbWFyeSRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCBhdXRvbHlzaXNfc3VtbWFyeSRUaXNzdWUpXSwKICAgICAgUklOX1JNU0UgPSByaW5fc3VtbWFyeSRSTVNFW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fc3VtbWFyeSRUaXNzdWUpXSwKICAgICAgQXV0b19STVNFID0gYXV0b2x5c2lzX3N1bW1hcnkkUk1TRVttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKV0KICAgICkKICAgIAogICAgcl9jb3JyZWxhdGlvbiA8LSBjb3IoYWJzKGNvbXBhcmlzb25fZGF0YSRSSU5fUiksIGFicyhjb21wYXJpc29uX2RhdGEkQXV0b19SKSwgdXNlID0gImNvbXBsZXRlLm9icyIpCiAgICAKICAgIGNvcnJlbGF0aW9uX3Bsb3QgPC0gZ2dwbG90KGNvbXBhcmlzb25fZGF0YSwgYWVzKHggPSBhYnMoUklOX1IpLCB5ID0gYWJzKEF1dG9fUikpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSwgYWxwaGEgPSAwLjgsIGNvbG9yID0gImRhcmtibHVlIikgKwogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gInJlZCIsIGFscGhhID0gMC4zKSArCiAgICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAxKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQS4gQ3Jvc3MtTW9kZWwgUGVyZm9ybWFuY2UgQ29ycmVsYXRpb24iLAogICAgICAgIHggPSAiUklOIE1vZGVsIHxSfCIsCiAgICAgICAgeSA9ICJBdXRvbHlzaXMgTW9kZWwgfFJ8IiwKICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQocl9jb3JyZWxhdGlvbiwgMykpCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApICsKICAgICAgY29vcmRfZml4ZWQoKQogICAgCiAgICByZXR1cm4oY29ycmVsYXRpb25fcGxvdCkKICB9CiAgCiAgY3JlYXRlX3NhbXBsZV9zaXplX3BlcmZvcm1hbmNlIDwtIGZ1bmN0aW9uKCkgewogICAgY29tYmluZWRfZGF0YSA8LSByYmluZCgKICAgICAgZGF0YS5mcmFtZShyaW5fc3VtbWFyeVssIGMoIlRpc3N1ZSIsICJTYW1wbGVzIiwgIlIiKV0sIE1vZGVsID0gIlJJTiIpLAogICAgICBkYXRhLmZyYW1lKGF1dG9seXNpc19zdW1tYXJ5WywgYygiVGlzc3VlIiwgIlNhbXBsZXMiLCAiUiIpXSwgTW9kZWwgPSAiQXV0b2x5c2lzIikKICAgICkKICAgIAogICAgY29tYmluZWRfZGF0YSRSX2FicyA8LSBhYnMoY29tYmluZWRfZGF0YSRSKQogICAgY29tYmluZWRfZGF0YSRTaXplX0JpbiA8LSBjdXQoY29tYmluZWRfZGF0YSRTYW1wbGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1MCwgMTAwLCAyMDAsIDUwMCwgSW5mKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygi4omkNTAiLCAiNTEtMTAwIiwgIjEwMS0yMDAiLCAiMjAxLTUwMCIsICI+NTAwIikpCiAgICAKICAgIHNpemVfcGVyZm9ybWFuY2VfcGxvdCA8LSBnZ3Bsb3QoY29tYmluZWRfZGF0YSwgYWVzKHggPSBTaXplX0JpbiwgeSA9IFJfYWJzLCBmaWxsID0gTW9kZWwpKSArCiAgICAgIGdlb21fYm94cGxvdChhbHBoYSA9IDAuOCwgb3V0bGllci5hbHBoYSA9IDAuNikgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBNb2RlbCBQZXJmb3JtYW5jZSBieSBTYW1wbGUgU2l6ZSBDYXRlZ29yeSIsCiAgICAgICAgeCA9ICJTYW1wbGUgU2l6ZSBDYXRlZ29yeSIsCiAgICAgICAgeSA9ICJBYnNvbHV0ZSBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCB8UnwiLAogICAgICAgIHN1YnRpdGxlID0gIlBlcmZvcm1hbmNlIGRpc3RyaWJ1dGlvbiBhY3Jvc3Mgc2FtcGxlIHNpemUgYmlucyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHNpemVfcGVyZm9ybWFuY2VfcGxvdCkKICB9CiAgCiAgY3JlYXRlX3Rpc3N1ZV9wcmVmZXJlbmNlX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgY29tbW9uX3Rpc3N1ZXMgPC0gaW50ZXJzZWN0KHJpbl9zdW1tYXJ5JFRpc3N1ZSwgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKQogICAgCiAgICBwcmVmZXJlbmNlX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgICAgVGlzc3VlID0gY29tbW9uX3Rpc3N1ZXMsCiAgICAgIFJJTl9SID0gYWJzKHJpbl9zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIHJpbl9zdW1tYXJ5JFRpc3N1ZSldKSwKICAgICAgQXV0b19SID0gYWJzKGF1dG9seXNpc19zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSldKQogICAgKQogICAgCiAgICBwcmVmZXJlbmNlX2RhdGEkQmV0dGVyX01vZGVsIDwtIGlmZWxzZSgKICAgICAgcHJlZmVyZW5jZV9kYXRhJFJJTl9SID4gcHJlZmVyZW5jZV9kYXRhJEF1dG9fUiwgIlJJTiIsICJBdXRvbHlzaXMiCiAgICApCiAgICBwcmVmZXJlbmNlX2RhdGEkUGVyZm9ybWFuY2VfRGlmZmVyZW5jZSA8LSBhYnMocHJlZmVyZW5jZV9kYXRhJFJJTl9SIC0gcHJlZmVyZW5jZV9kYXRhJEF1dG9fUikKICAgIHByZWZlcmVuY2VfZGF0YSRNYXhfUiA8LSBwbWF4KHByZWZlcmVuY2VfZGF0YSRSSU5fUiwgcHJlZmVyZW5jZV9kYXRhJEF1dG9fUikKICAgIAogICAgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZV9TeXN0ZW0gPC0gY2FzZV93aGVuKAogICAgICBncmVwbCgiQnJhaW58TmVydmV8U3BpbmFsIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiTmVydm91cyIsCiAgICAgIGdyZXBsKCJIZWFydHxBcnRlcnl8QW9ydGEiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJDYXJkaW92YXNjdWxhciIsIAogICAgICBncmVwbCgiTHVuZ3xUcmFjaGVhIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiUmVzcGlyYXRvcnkiLAogICAgICBncmVwbCgiTGl2ZXJ8UGFuY3JlYXN8U3RvbWFjaHxDb2xvbnxTbWFsbHxFc29waGFndXMiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJEaWdlc3RpdmUiLAogICAgICBncmVwbCgiS2lkbmV5fEJsYWRkZXIiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJVcmluYXJ5IiwKICAgICAgZ3JlcGwoIk11c2NsZXxTa2luIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiTXVzY3Vsb3NrZWxldGFsIiwKICAgICAgZ3JlcGwoIlRoeXJvaWR8QWRyZW5hbHxQaXR1aXRhcnkiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJFbmRvY3JpbmUiLAogICAgICBUUlVFIH4gIk90aGVyIgogICAgKQogICAgCiAgICBwcmVmZXJlbmNlX3Bsb3QgPC0gZ2dwbG90KHByZWZlcmVuY2VfZGF0YSwgYWVzKHggPSBNYXhfUiwgeSA9IFBlcmZvcm1hbmNlX0RpZmZlcmVuY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQmV0dGVyX01vZGVsLCBzaGFwZSA9IFRpc3N1ZV9TeXN0ZW0pKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGFscGhhID0gMC44KSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTYsIDE3LCAxOCwgMTUsIDMsIDQsIDgsIDEpWzE6bGVuZ3RoKHVuaXF1ZShwcmVmZXJlbmNlX2RhdGEkVGlzc3VlX1N5c3RlbSkpXSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkMuIFRpc3N1ZSBDYXRlZ29yaWVzIGFuZCBNb2RlbCBQcmVmZXJlbmNlIiwKICAgICAgICB4ID0gIk1heGltdW0gfFJ8IFZhbHVlIiwKICAgICAgICB5ID0gIkFic29sdXRlIERpZmZlcmVuY2UgaW4gfFJ8IiwKICAgICAgICBjb2xvciA9ICJCZXR0ZXIgTW9kZWwiLAogICAgICAgIHNoYXBlID0gIlRpc3N1ZSBTeXN0ZW0iLAogICAgICAgIHN1YnRpdGxlID0gIk1vZGVsIHByZWZlcmVuY2UgcGF0dGVybnMgYWNyb3NzIGJpb2xvZ2ljYWwgc3lzdGVtcyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKQogICAgCiAgICByZXR1cm4ocHJlZmVyZW5jZV9wbG90KQogIH0KICAKICBjcmVhdGVfZmVhdHVyZV9vdmVybGFwX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgZXh0cmFjdF9mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgdGhyZXNob2xkID0gNSkgewogICAgICBmZWF0dXJlX2NvdW50cyA8LSBsaXN0KCkKICAgICAgZm9yKHRpc3N1ZSBpbiBuYW1lcyh0YXJnZXRfcmVzdWx0cykpIHsKICAgICAgICBpZighaXMubnVsbCh0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMpKSB7CiAgICAgICAgICBjb3VudHMgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzCiAgICAgICAgICBmb3IoZmVhdCBpbiBuYW1lcyhjb3VudHMpKSB7CiAgICAgICAgICAgIGlmKGlzLm51bGwoZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSkpIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gPC0gMAogICAgICAgICAgICBmZWF0dXJlX2NvdW50c1tbZmVhdF1dIDwtIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gKyBjb3VudHNbW2ZlYXRdXQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgaW1wb3J0YW50X2ZlYXR1cmVzIDwtIG5hbWVzKGZlYXR1cmVfY291bnRzKVtmZWF0dXJlX2NvdW50cyA+PSB0aHJlc2hvbGRdCiAgICAgIHJldHVybihpbXBvcnRhbnRfZmVhdHVyZXMpCiAgICB9CiAgICAKICAgIHRocmVzaG9sZHMgPC0gYygzLCA1LCAxMCwgMTUsIDIwKQogICAgb3ZlcmxhcF9kYXRhIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IodGhyZXNoIGluIHRocmVzaG9sZHMpIHsKICAgICAgcmluX3RocmVzaCA8LSBleHRyYWN0X2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIHRocmVzaCkKICAgICAgYXV0b190aHJlc2ggPC0gZXh0cmFjdF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dLCB0aHJlc2gpCiAgICAgIAogICAgICBvdmVybGFwX2NvdW50IDwtIGxlbmd0aChpbnRlcnNlY3QocmluX3RocmVzaCwgYXV0b190aHJlc2gpKQogICAgICByaW5fb25seSA8LSBsZW5ndGgoc2V0ZGlmZihyaW5fdGhyZXNoLCBhdXRvX3RocmVzaCkpCiAgICAgIGF1dG9fb25seSA8LSBsZW5ndGgoc2V0ZGlmZihhdXRvX3RocmVzaCwgcmluX3RocmVzaCkpCiAgICAgIAogICAgICBvdmVybGFwX2RhdGEgPC0gcmJpbmQob3ZlcmxhcF9kYXRhLCBkYXRhLmZyYW1lKAogICAgICAgIFRocmVzaG9sZCA9IHRocmVzaCwKICAgICAgICBPdmVybGFwID0gb3ZlcmxhcF9jb3VudCwKICAgICAgICBSSU5fT25seSA9IHJpbl9vbmx5LAogICAgICAgIEF1dG9fT25seSA9IGF1dG9fb25seQogICAgICApKQogICAgfQogICAgCiAgICBvdmVybGFwX2xvbmcgPC0gcmVzaGFwZTI6Om1lbHQob3ZlcmxhcF9kYXRhLCBpZC52YXJzID0gIlRocmVzaG9sZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJDYXRlZ29yeSIsIHZhbHVlLm5hbWUgPSAiQ291bnQiKQogICAgCiAgICBvdmVybGFwX3Bsb3QgPC0gZ2dwbG90KG92ZXJsYXBfbG9uZywgYWVzKHggPSBmYWN0b3IoVGhyZXNob2xkKSwgeSA9IENvdW50LCBmaWxsID0gQ2F0ZWdvcnkpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiT3ZlcmxhcCIgPSAiIzhFNDRBRCIsICJSSU5fT25seSIgPSAiIzY2Q0RBQSIsICJBdXRvX09ubHkiID0gIiNGQzhENjIiKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJDb21tb24gRmVhdHVyZXMiLCAiUklOIE9ubHkiLCAiQXV0b2x5c2lzIE9ubHkiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkQuIEZlYXR1cmUgT3ZlcmxhcCBhdCBEaWZmZXJlbnQgU2VsZWN0aW9uIFRocmVzaG9sZHMiLAogICAgICAgIHggPSAiTWluaW11bSBTZWxlY3Rpb24gRnJlcXVlbmN5IiwKICAgICAgICB5ID0gIk51bWJlciBvZiBGZWF0dXJlcyIsCiAgICAgICAgZmlsbCA9ICJGZWF0dXJlIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIk92ZXJsYXAgYW5hbHlzaXMgYWNyb3NzIHNlbGVjdGlvbiBmcmVxdWVuY3kgdGhyZXNob2xkcyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKG92ZXJsYXBfcGxvdCkKICB9CiAgCiAgcGFuZWxfYSA8LSBjcmVhdGVfY3Jvc3NfbW9kZWxfY29ycmVsYXRpb24oKQogIHBhbmVsX2IgPC0gY3JlYXRlX3NhbXBsZV9zaXplX3BlcmZvcm1hbmNlKCkKICBwYW5lbF9jIDwtIGNyZWF0ZV90aXNzdWVfcHJlZmVyZW5jZV9hbmFseXNpcygpCiAgcGFuZWxfZCA8LSBjcmVhdGVfZmVhdHVyZV9vdmVybGFwX2FuYWx5c2lzKCkKICAKICBjb21iaW5lZF9wbG90IDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBwYW5lbF9jLCBwYW5lbF9kLCBucm93ID0gMiwgbmNvbCA9IDIpCiAgCiAgcmV0dXJuKGNvbWJpbmVkX3Bsb3QpCn0KCmNyZWF0ZV9tb2RlbF9jb21wYXJpc29uX3N1bW1hcnkgPC0gZnVuY3Rpb24ocmluX3N1bW1hcnksIGF1dG9seXNpc19zdW1tYXJ5KSB7CiAgY29tbW9uX3Rpc3N1ZXMgPC0gaW50ZXJzZWN0KHJpbl9zdW1tYXJ5JFRpc3N1ZSwgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKQogIAogIGNvbXBhcmlzb25fc3VtbWFyeSA8LSBkYXRhLmZyYW1lKAogICAgVGlzc3VlID0gY29tbW9uX3Rpc3N1ZXMsCiAgICBSSU5fUiA9IGFicyhyaW5fc3VtbWFyeSRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fc3VtbWFyeSRUaXNzdWUpXSksCiAgICBBdXRvX1IgPSBhYnMoYXV0b2x5c2lzX3N1bW1hcnkkUlttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKV0pCiAgKQogIAogIGNvbXBhcmlzb25fc3VtbWFyeSRCZXR0ZXJfTW9kZWwgPC0gaWZlbHNlKAogICAgY29tcGFyaXNvbl9zdW1tYXJ5JFJJTl9SID4gY29tcGFyaXNvbl9zdW1tYXJ5JEF1dG9fUiwgIlJJTiIsICJBdXRvbHlzaXMiCiAgKQogIAogIG1vZGVsX3ByZWZlcmVuY2UgPC0gdGFibGUoY29tcGFyaXNvbl9zdW1tYXJ5JEJldHRlcl9Nb2RlbCkKICAKICByZXR1cm4obGlzdCgKICAgIHN1bW1hcnkgPSBjb21wYXJpc29uX3N1bW1hcnksCiAgICBwcmVmZXJlbmNlX2NvdW50cyA9IG1vZGVsX3ByZWZlcmVuY2UsCiAgICBtZWFuX3BlcmZvcm1hbmNlID0gYygKICAgICAgUklOID0gbWVhbihjb21wYXJpc29uX3N1bW1hcnkkUklOX1IsIG5hLnJtID0gVFJVRSksCiAgICAgIEF1dG9seXNpcyA9IG1lYW4oY29tcGFyaXNvbl9zdW1tYXJ5JEF1dG9fUiwgbmEucm0gPSBUUlVFKQogICAgKQogICkpCn0KCnN1cHBfZmlndXJlX3MzIDwtIGNyZWF0ZV9leHRlbmRlZF9jb21wYXJhdGl2ZV9hbmFseXNpcyhhbGxfcmVzdWx0cywgcmluX3N1bW1hcnksIGF1dG9seXNpc19zdW1tYXJ5KQpjb21wYXJpc29uX3N1bW1hcnkgPC0gY3JlYXRlX21vZGVsX2NvbXBhcmlzb25fc3VtbWFyeShyaW5fc3VtbWFyeSwgYXV0b2x5c2lzX3N1bW1hcnkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiU3VwcEZpZ19TM19FeHRlbmRlZF9Db21wYXJpc29uLnBkZiIpLCAKICAgICAgIHN1cHBfZmlndXJlX3MzLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSAxNCwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDE1LiBDb21wcmVoZW5zaXZlIEZpbmFsIFJlcG9ydCBHZW5lcmF0aW9uCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmdlbmVyYXRlX2NvbXByZWhlbnNpdmVfcmVwb3J0IDwtIGZ1bmN0aW9uKCkgewogIHJlcG9ydF9wYXRoIDwtIGZpbGUucGF0aChkYXRhX3BhdGgsICJwcm9jZXNzZWQvY29tcHJlaGVuc2l2ZV9ndGV4X3JlcG9ydC50eHQiKQogIAogIHNpbmsocmVwb3J0X3BhdGgpCiAgCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIiAgICAgICAgICAgICAgICAgICAgQ09NUFJFSEVOU0lWRSBHVEV4IEFOQUxZU0lTIFJFUE9SVCAgICAgICAgICAgICAgICAgICAgICAgICAgXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuIikKICAKICAjIEhlYWRlciBJbmZvcm1hdGlvbgogIGNhdCgiQU5BTFlTSVMgT1ZFUlZJRVdcbiIpCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLVxuIikKICBjYXQocGFzdGUoIlJlcG9ydCBHZW5lcmF0ZWQ6IiwgU3lzLnRpbWUoKSwgIlxuIikpCiAgY2F0KHBhc3RlKCJQaXBlbGluZSBWZXJzaW9uOiBHVEV4IEFuYWx5c2lzIFBpcGVsaW5lIHYxLjBcbiIpKQogIGNhdChwYXN0ZSgiQW5hbHlzaXMgRHVyYXRpb246IiwgZm9ybWF0KFN5cy50aW1lKCkgLSBzdGFydF90aW1lLCBkaWdpdHMgPSAzKSwgIlxuXG4iKSkKICAKICAjIERhdGFzZXQgU3VtbWFyeQogIGNhdCgiREFUQVNFVCBTVU1NQVJZXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09XG4iKQogIGNhdChwYXN0ZSgiVG90YWwgU2FtcGxlcyBBbmFseXplZDoiLCBucm93KG1lcmdlZF9ndGV4KSwgIlxuIikpCiAgY2F0KHBhc3RlKCJUb3RhbCBUaXNzdWUgVHlwZXM6IiwgbGVuZ3RoKHVuaXF1ZShtZXJnZWRfZ3RleCR0aXNzdWVfbWFpbikpLCAiXG4iKSkKICBjYXQocGFzdGUoIlRvdGFsIEZlYXR1cmVzIEV4dHJhY3RlZDoiLCBsZW5ndGgoZ3JlcCgiXmZlYXR1cmVfIiwgbmFtZXMobWVyZ2VkX2d0ZXgpKSksICJcbiIpKQogIGNhdChwYXN0ZSgiTWFsZSBTdWJqZWN0czoiLCBzdW0obWVyZ2VkX2d0ZXgkc2V4ID09ICJtYWxlIiksICJcbiIpKQogIGNhdChwYXN0ZSgiRmVtYWxlIFN1YmplY3RzOiIsIHN1bShtZXJnZWRfZ3RleCRzZXggPT0gImZlbWFsZSIpLCAiXG5cbiIpKQogIAogICMgVG9wIHRpc3N1ZSB0eXBlcyBieSBzYW1wbGUgY291bnQKICB0aXNzdWVfY291bnRzIDwtIG1lcmdlZF9ndGV4WywgLk4sIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKC1OKV0KICBjYXQoIlRvcCAxMCBUaXNzdWUgVHlwZXMgYnkgU2FtcGxlIENvdW50OlxuIikKICBmb3IoaSBpbiAxOm1pbigxMCwgbnJvdyh0aXNzdWVfY291bnRzKSkpIHsKICAgIGNhdChzcHJpbnRmKCIgICUyZC4gJS0yNXM6ICU0ZCBzYW1wbGVzXG4iLCBpLCB0aXNzdWVfY291bnRzJHRpc3N1ZV9tYWluW2ldLCB0aXNzdWVfY291bnRzJE5baV0pKQogIH0KICBjYXQoIlxuIikKICAKICAjIFF1YWxpdHkgTWV0cmljcyBBbmFseXNpcwogIGNhdCgiUVVBTElUWSBNRVRSSUNTIEFOQUxZU0lTXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09XG4iKQogIAogICMgUklOIEFuYWx5c2lzCiAgcmluX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNUklOKV0KICBpZihucm93KHJpbl9kYXRhKSA+IDApIHsKICAgIGNhdCgiUklOIFNjb3JlIERpc3RyaWJ1dGlvbjpcbiIpCiAgICBjYXQocGFzdGUoIiAgU2FtcGxlcyB3aXRoIFJJTiBkYXRhOiIsIG5yb3cocmluX2RhdGEpLCAib2YiLCBucm93KG1lcmdlZF9ndGV4KSwgCiAgICAgICAgICAgICAgc3ByaW50ZigiKCUuMWYlJSlcbiIsIDEwMCAqIG5yb3cocmluX2RhdGEpIC8gbnJvdyhtZXJnZWRfZ3RleCkpKSkKICAgIGNhdChwYXN0ZSgiICBNZWFuIFJJTiBTY29yZToiLCByb3VuZChtZWFuKHJpbl9kYXRhJFNNUklOKSwgMiksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIE1lZGlhbiBSSU4gU2NvcmU6Iiwgcm91bmQobWVkaWFuKHJpbl9kYXRhJFNNUklOKSwgMiksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIFJJTiBSYW5nZToiLCByb3VuZChtaW4ocmluX2RhdGEkU01SSU4pLCAyKSwgIi0iLCByb3VuZChtYXgocmluX2RhdGEkU01SSU4pLCAyKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgSGlnaCBRdWFsaXR5IChSSU4g4omlIDcuMCk6Iiwgc3VtKHJpbl9kYXRhJFNNUklOID49IDcuMCksIAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBzdW0ocmluX2RhdGEkU01SSU4gPj0gNy4wKSAvIG5yb3cocmluX2RhdGEpKSkpCiAgfQogIAogICMgQXV0b2x5c2lzIEFuYWx5c2lzCiAgYXV0b2x5c2lzX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNQVRTU0NSKV0KICBpZihucm93KGF1dG9seXNpc19kYXRhKSA+IDApIHsKICAgIGNhdCgiXG5BdXRvbHlzaXMgU2NvcmUgRGlzdHJpYnV0aW9uOlxuIikKICAgIGNhdChwYXN0ZSgiICBTYW1wbGVzIHdpdGggQXV0b2x5c2lzIGRhdGE6IiwgbnJvdyhhdXRvbHlzaXNfZGF0YSksICJvZiIsIG5yb3cobWVyZ2VkX2d0ZXgpLAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBucm93KGF1dG9seXNpc19kYXRhKSAvIG5yb3cobWVyZ2VkX2d0ZXgpKSkpCiAgICBjYXQocGFzdGUoIiAgTWVhbiBBdXRvbHlzaXMgU2NvcmU6Iiwgcm91bmQobWVhbihhdXRvbHlzaXNfZGF0YSRTTUFUU1NDUiksIDIpLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBNZWRpYW4gQXV0b2x5c2lzIFNjb3JlOiIsIHJvdW5kKG1lZGlhbihhdXRvbHlzaXNfZGF0YSRTTUFUU1NDUiksIDIpLCAiXG4iKSkKICAgIGF1dG9seXNpc19jb3VudHMgPC0gdGFibGUoYXV0b2x5c2lzX2RhdGEkU01BVFNTQ1IpCiAgICBmb3Ioc2NvcmUgaW4gbmFtZXMoYXV0b2x5c2lzX2NvdW50cykpIHsKICAgICAgY2F0KHNwcmludGYoIiAgU2NvcmUgJXM6ICVkIHNhbXBsZXMgKCUuMWYlJSlcbiIsIHNjb3JlLCBhdXRvbHlzaXNfY291bnRzW3Njb3JlXSwKICAgICAgICAgICAgICAgICAgMTAwICogYXV0b2x5c2lzX2NvdW50c1tzY29yZV0gLyBucm93KGF1dG9seXNpc19kYXRhKSkpCiAgICB9CiAgICBjYXQocGFzdGUoIiAgSGlnaCBRdWFsaXR5IChTY29yZSDiiaQgMik6Iiwgc3VtKGF1dG9seXNpc19kYXRhJFNNQVRTU0NSIDw9IDIpLAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBzdW0oYXV0b2x5c2lzX2RhdGEkU01BVFNTQ1IgPD0gMikgLyBucm93KGF1dG9seXNpc19kYXRhKSkpKQogIH0KICAKICAjIERpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiBSZXN1bHRzCiAgY2F0KCJcbkRJTUVOU0lPTkFMSVRZIFJFRFVDVElPTiBBTkFMWVNJU1xuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICAKICBpZihleGlzdHMoInBjYV9yZXN1bHRzIikpIHsKICAgIGNhdCgiUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpczpcbiIpCiAgICBjYXQocGFzdGUoIiAgUEMxIFZhcmlhbmNlIEV4cGxhaW5lZDoiLCBzcHJpbnRmKCIlLjFmJSUiLCBwY2FfcmVzdWx0cyR2YXJpYW5jZV9leHBsYWluZWRbMV0gKiAxMDApLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBQQzIgVmFyaWFuY2UgRXhwbGFpbmVkOiIsIHNwcmludGYoIiUuMWYlJSIsIHBjYV9yZXN1bHRzJHZhcmlhbmNlX2V4cGxhaW5lZFsyXSAqIDEwMCksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIEZpcnN0IDEwIFBDcyBDdW11bGF0aXZlIFZhcmlhbmNlOiIsIHNwcmludGYoIiUuMWYlJSIsIHBjYV9yZXN1bHRzJGN1bXVsYXRpdmVfdmFyaWFuY2VbMTBdICogMTAwKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgRmlyc3QgNTAgUENzIEN1bXVsYXRpdmUgVmFyaWFuY2U6Iiwgc3ByaW50ZigiJS4xZiUlIiwgcGNhX3Jlc3VsdHMkY3VtdWxhdGl2ZV92YXJpYW5jZVs1MF0gKiAxMDApLCAiXG4iKSkKICB9CiAgCiAgaWYoZXhpc3RzKCJ0aXNzdWVfc2lsIikpIHsKICAgIGNhdCgiXG5VTUFQIENsdXN0ZXJpbmcgQW5hbHlzaXM6XG4iKQogICAgY2F0KHBhc3RlKCIgIE92ZXJhbGwgU2lsaG91ZXR0ZSBTY29yZToiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihzaWxbLDNdLCBuYS5ybSA9IFRSVUUpKSwgIlxuIikpCiAgICBjYXQoIiAgVG9wIDUgQmVzdC1TZXBhcmF0ZWQgVGlzc3VlcyAoYnkgU2lsaG91ZXR0ZSBTY29yZSk6XG4iKQogICAgdG9wX3NpbGhvdWV0dGUgPC0gaGVhZCh0aXNzdWVfc2lsW29yZGVyKC1hdmdfc2lsaG91ZXR0ZSldLCA1KQogICAgZm9yKGkgaW4gMTpucm93KHRvcF9zaWxob3VldHRlKSkgewogICAgICBjYXQoc3ByaW50ZigiICAgICVkLiAlLTI1czogJS4zZlxuIiwgaSwgdG9wX3NpbGhvdWV0dGUkdGlzc3VlW2ldLCB0b3Bfc2lsaG91ZXR0ZSRhdmdfc2lsaG91ZXR0ZVtpXSkpCiAgICB9CiAgfQogIAogICMgVmFyaWFuY2UgQW5hbHlzaXMgUmVzdWx0cwogIGNhdCgiXG5WQVJJQU5DRSBBTkFMWVNJUyAoQU5PVkEpXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICAKICBpZihleGlzdHMoInJpbl92YXJpYW5jZSIpKSB7CiAgICBjYXQoIlJJTiBTY29yZSBWYXJpYW5jZSBFeHBsYWluZWQgYnkgRGVtb2dyYXBoaWNzOlxuIikKICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQgPC0gcmluX3ZhcmlhbmNlW29yZGVyKC1yaW5fdmFyaWFuY2UkdmFyX2V4cGxhaW5lZCksXQogICAgZm9yKGkgaW4gMTpucm93KHJpbl92YXJpYW5jZV9zb3J0ZWQpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICUtMjBzOiAlNS4xZiUlIChwICVzKVxuIiwgCiAgICAgICAgICAgICAgICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQkbGFiZWxbaV0sIAogICAgICAgICAgICAgICAgICByaW5fdmFyaWFuY2Vfc29ydGVkJHZhcl9leHBsYWluZWRbaV0sCiAgICAgICAgICAgICAgICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQkc2lnbmlmaWNhbmNlW2ldKSkKICAgIH0KICB9CiAgCiAgaWYoZXhpc3RzKCJhdXRvbHlzaXNfdmFyaWFuY2UiKSkgewogICAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBWYXJpYW5jZSBFeHBsYWluZWQgYnkgRGVtb2dyYXBoaWNzOlxuIikKICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQgPC0gYXV0b2x5c2lzX3ZhcmlhbmNlW29yZGVyKC1hdXRvbHlzaXNfdmFyaWFuY2UkdmFyX2V4cGxhaW5lZCksXQogICAgZm9yKGkgaW4gMTpucm93KGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICUtMjBzOiAlNS4xZiUlIChwICVzKVxuIiwgCiAgICAgICAgICAgICAgICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQkbGFiZWxbaV0sIAogICAgICAgICAgICAgICAgICBhdXRvbHlzaXNfdmFyaWFuY2Vfc29ydGVkJHZhcl9leHBsYWluZWRbaV0sCiAgICAgICAgICAgICAgICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQkc2lnbmlmaWNhbmNlW2ldKSkKICAgIH0KICB9CiAgCiAgIyBDb3JyZWxhdGlvbiBBbmFseXNpcwogIGNhdCgiXG5DT1JSRUxBVElPTiBBTkFMWVNJU1xuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09XG4iKQogIAogIGlmKGV4aXN0cygidGlzc3VlX2NvcnIiKSkgewogICAgY2F0KCJSSU4gdnMgQXV0b2x5c2lzIENvcnJlbGF0aW9ucyBieSBUaXNzdWU6XG4iKQogICAgdGlzc3VlX2NvcnJfc29ydGVkIDwtIHRpc3N1ZV9jb3JyW29yZGVyKC1hYnModGlzc3VlX2NvcnIkY29ycmVsYXRpb24pKSxdCiAgICBjYXQoIiAgU3Ryb25nZXN0IENvcnJlbGF0aW9ucyAoVG9wIDEwKTpcbiIpCiAgICB0b3BfY29yciA8LSBoZWFkKHRpc3N1ZV9jb3JyX3NvcnRlZCwgMTApCiAgICBmb3IoaSBpbiAxOm5yb3codG9wX2NvcnIpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICAgJS0yNXM6IHIgPSAlNi4zZiAocCAlcywgbiA9ICVkKVxuIiwgCiAgICAgICAgICAgICAgICAgIHRvcF9jb3JyJHRpc3N1ZV9tYWluW2ldLCAKICAgICAgICAgICAgICAgICAgdG9wX2NvcnIkY29ycmVsYXRpb25baV0sCiAgICAgICAgICAgICAgICAgIHRvcF9jb3JyJHNpZ25pZmljYW5jZVtpXSwKICAgICAgICAgICAgICAgICAgdG9wX2NvcnIkbl9zYW1wbGVzW2ldKSkKICAgIH0KICB9CiAgCiAgIyBNYWNoaW5lIExlYXJuaW5nIE1vZGVsIFJlc3VsdHMKICBjYXQoIlxuTUFDSElORSBMRUFSTklORyBNT0RFTCBQRVJGT1JNQU5DRVxuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgaWYoZXhpc3RzKCJhbGxfcmVzdWx0cyIpKSB7CiAgICAjIFJJTiBNb2RlbHMKICAgIGlmKCJTTVJJTiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICAgICAgcmluX21vZGVscyA8LSBhbGxfcmVzdWx0c1tbIlNNUklOIl1dCiAgICAgIHJpbl9yX3ZhbHVlcyA8LSBzYXBwbHkocmluX21vZGVscywgZnVuY3Rpb24oeCkgewogICAgICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgICAgICByZXR1cm4oYWJzKHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IpKSAgIyBVc2UgYWJzb2x1dGUgUgogICAgICAgICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICAgICAgfSkKICAgICAgcmluX3JfdmFsdWVzIDwtIHJpbl9yX3ZhbHVlc1shaXMubmEocmluX3JfdmFsdWVzKV0KICAgICAgCiAgICAgIGNhdCgiUklOIFNjb3JlIFByZWRpY3Rpb24gTW9kZWxzOlxuIikKICAgICAgY2F0KHBhc3RlKCIgIFN1Y2Nlc3NmdWwgbW9kZWxzOiIsIGxlbmd0aChyaW5fcl92YWx1ZXMpLCAidGlzc3Vlc1xuIikpCiAgICAgIGNhdChwYXN0ZSgiICBNZWFuIHxSfDoiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihyaW5fcl92YWx1ZXMpKSwgIlxuIikpCiAgICAgIGNhdChwYXN0ZSgiICBNZWRpYW4gfFJ8OiIsIHNwcmludGYoIiUuM2YiLCBtZWRpYW4ocmluX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgU3Ryb25nIG1vZGVscyAofFJ8ID4gMC41KToiLCBzdW0ocmluX3JfdmFsdWVzID4gMC41KSwgIlxuIikpCiAgICAgIGNhdChwYXN0ZSgiICBFeGNlbGxlbnQgbW9kZWxzICh8UnwgPiAwLjcpOiIsIHN1bShyaW5fcl92YWx1ZXMgPiAwLjcpLCAiXG4iKSkKICAgICAgCiAgICAgICMgVG9wIHBlcmZvcm1pbmcgdGlzc3VlcwogICAgICBjYXQoIiAgVG9wIDUgUklOIFByZWRpY3Rpb24gTW9kZWxzOlxuIikKICAgICAgdG9wX3JpbiA8LSBuYW1lcyhzb3J0KHJpbl9yX3ZhbHVlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCiAgICAgIGZvcihpIGluIDE6bGVuZ3RoKHRvcF9yaW4pKSB7CiAgICAgICAgdGlzc3VlX25hbWUgPC0gdG9wX3JpbltpXQogICAgICAgIHJfdmFsdWUgPC0gcmluX3JfdmFsdWVzW3Rpc3N1ZV9uYW1lXQogICAgICAgIGNhdChzcHJpbnRmKCIgICAgJWQuICUtMjVzOiB8UnwgPSAlLjNmXG4iLCBpLCB0aXNzdWVfbmFtZSwgcl92YWx1ZSkpCiAgICAgIH0KICAgIH0KICAgIAogICAgIyBBdXRvbHlzaXMgTW9kZWxzCiAgICBpZigiU01BVFNTQ1IiICVpbiUgbmFtZXMoYWxsX3Jlc3VsdHMpKSB7CiAgICAgIGF1dG9fbW9kZWxzIDwtIGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0KICAgICAgYXV0b19yX3ZhbHVlcyA8LSBzYXBwbHkoYXV0b19tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgICAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oTkEpCiAgICAgICAgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKQogICAgICB9KQogICAgICBhdXRvX3JfdmFsdWVzIDwtIGF1dG9fcl92YWx1ZXNbIWlzLm5hKGF1dG9fcl92YWx1ZXMpXQogICAgICAKICAgICAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBQcmVkaWN0aW9uIE1vZGVsczpcbiIpCiAgICAgIGNhdChwYXN0ZSgiICBTdWNjZXNzZnVsIG1vZGVsczoiLCBsZW5ndGgoYXV0b19yX3ZhbHVlcyksICJ0aXNzdWVzXG4iKSkKICAgICAgY2F0KHBhc3RlKCIgIE1lYW4gUjoiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihhdXRvX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgTWVkaWFuIFI6Iiwgc3ByaW50ZigiJS4zZiIsIG1lZGlhbihhdXRvX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgU3Ryb25nIG1vZGVscyAoUiA+IDAuNSk6Iiwgc3VtKGF1dG9fcl92YWx1ZXMgPiAwLjUpLCAiXG4iKSkKICAgICAgY2F0KHBhc3RlKCIgIEV4Y2VsbGVudCBtb2RlbHMgKFIgPiAwLjcpOiIsIHN1bShhdXRvX3JfdmFsdWVzID4gMC43KSwgIlxuIikpCiAgICAgIAogICAgICAjIFRvcCBwZXJmb3JtaW5nIHRpc3N1ZXMKICAgICAgY2F0KCIgIFRvcCA1IEF1dG9seXNpcyBQcmVkaWN0aW9uIE1vZGVsczpcbiIpCiAgICAgIHRvcF9hdXRvIDwtIG5hbWVzKHNvcnQoYXV0b19yX3ZhbHVlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCiAgICAgIGZvcihpIGluIDE6bGVuZ3RoKHRvcF9hdXRvKSkgewogICAgICAgIHRpc3N1ZV9uYW1lIDwtIHRvcF9hdXRvW2ldCiAgICAgICAgcl92YWx1ZSA8LSBhdXRvX3JfdmFsdWVzW3Rpc3N1ZV9uYW1lXQogICAgICAgIGNhdChzcHJpbnRmKCIgICAgJWQuICUtMjVzOiBSID0gJS4zZlxuIiwgaSwgdGlzc3VlX25hbWUsIHJfdmFsdWUpKQogICAgICB9CiAgICB9CiAgfQogIAogICMgRmVhdHVyZSBBbmFseXNpcwogIGNhdCgiXG5GRUFUVVJFIElNUE9SVEFOQ0UgQU5BTFlTSVNcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgaWYoZXhpc3RzKCJjb21tb25fZmVhdHVyZXMiKSAmJiBleGlzdHMoInJpbl9vbmx5X2ZlYXR1cmVzIikgJiYgZXhpc3RzKCJhdXRvX29ubHlfZmVhdHVyZXMiKSkgewogICAgY2F0KCJGZWF0dXJlIE92ZXJsYXAgQW5hbHlzaXM6XG4iKQogICAgY2F0KHBhc3RlKCIgIENvbW1vbiBmZWF0dXJlcyAoYm90aCBtb2RlbHMpOiIsIGxlbmd0aChjb21tb25fZmVhdHVyZXMpLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBSSU4tc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgQXV0b2x5c2lzLXNwZWNpZmljIGZlYXR1cmVzOiIsIGxlbmd0aChhdXRvX29ubHlfZmVhdHVyZXMpLCAiXG4iKSkKICAgIAogICAgdG90YWxfdW5pcXVlIDwtIGxlbmd0aChjb21tb25fZmVhdHVyZXMpICsgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSArIGxlbmd0aChhdXRvX29ubHlfZmVhdHVyZXMpCiAgICBjYXQocGFzdGUoIiAgRmVhdHVyZSBvdmVybGFwIHBlcmNlbnRhZ2U6Iiwgc3ByaW50ZigiJS4xZiUlIiwgMTAwICogbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcykgLyB0b3RhbF91bmlxdWUpLCAiXG4iKSkKICB9CiAgCiAgIyBFc29waGFndXMgQW5hbHlzaXMKICBpZihleGlzdHMoImVzb3BoYWd1c19zYW1wbGVzIikpIHsKICAgIGNhdCgiXG5FU09QSEFHVVMgU1VCVFlQRSBBTkFMWVNJU1xuIikKICAgIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgICBlc29waGFndXNfY291bnRzIDwtIHRhYmxlKGVzb3BoYWd1c19zYW1wbGVzJHRpc3N1ZV9zdWIpCiAgICBjYXQoIkVzb3BoYWd1cyBTdWJ0eXBlIERpc3RyaWJ1dGlvbjpcbiIpCiAgICBmb3Ioc3VidHlwZSBpbiBuYW1lcyhlc29waGFndXNfY291bnRzKSkgewogICAgICBjYXQoc3ByaW50ZigiICAlLTMwczogJTNkIHNhbXBsZXNcbiIsIHN1YnR5cGUsIGVzb3BoYWd1c19jb3VudHNbc3VidHlwZV0pKQogICAgfQogIH0KICAKICAjIENyb3NzLVZhbGlkYXRpb24gU3RhYmlsaXR5CiAgaWYoZXhpc3RzKCJzdGFiaWxpdHlfc3RhdHMiKSkgewogICAgY2F0KCJcbk1PREVMIFNUQUJJTElUWSBBTkFMWVNJU1xuIikKICAgIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09XG4iKQogICAgCiAgICBzdGFibGVfcmluIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkY3ZfcltzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTVJJTiJdIDwgMC4yLCBuYS5ybSA9IFRSVUUpCiAgICB0b3RhbF9yaW4gPC0gc3VtKHN0YWJpbGl0eV9zdGF0cyR0YXJnZXQgPT0gIlNNUklOIikKICAgIHN0YWJsZV9hdXRvIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkY3ZfcltzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTUFUU1NDUiJdIDwgMC4yLCBuYS5ybSA9IFRSVUUpCiAgICB0b3RhbF9hdXRvIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTUFUU1NDUiIpCiAgICAKICAgIGNhdCgiQ3Jvc3MtVmFsaWRhdGlvbiBTdGFiaWxpdHkgKENWIDwgMC4yID0gU3RhYmxlKTpcbiIpCiAgICBjYXQoc3ByaW50ZigiICBSSU4gTW9kZWxzOiAlZC8lZCBzdGFibGUgKCUuMWYlJSlcbiIsIHN0YWJsZV9yaW4sIHRvdGFsX3JpbiwgMTAwICogc3RhYmxlX3JpbiAvIHRvdGFsX3JpbikpCiAgICBjYXQoc3ByaW50ZigiICBBdXRvbHlzaXMgTW9kZWxzOiAlZC8lZCBzdGFibGUgKCUuMWYlJSlcbiIsIHN0YWJsZV9hdXRvLCB0b3RhbF9hdXRvLCAxMDAgKiBzdGFibGVfYXV0byAvIHRvdGFsX2F1dG8pKQogIH0KICAKICAjIFN1bW1hcnkgU3RhdGlzdGljcwogIGNhdCgiXG5TVU1NQVJZIFNUQVRJU1RJQ1NcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgIyBGaWxlcyBnZW5lcmF0ZWQKICBvdXRwdXRfZmlsZXMgPC0gbGlzdC5maWxlcyhvdXRwdXRfcGF0aCwgcGF0dGVybiA9ICIqLnBkZiIsIGZ1bGwubmFtZXMgPSBGQUxTRSkKICBjYXQocGFzdGUoIkdlbmVyYXRlZCIsIGxlbmd0aChvdXRwdXRfZmlsZXMpLCAidmlzdWFsaXphdGlvbiBmaWxlczpcbiIpKQogIGZvcihmaWxlIGluIG91dHB1dF9maWxlcykgewogICAgY2F0KHBhc3RlKCIgIC0iLCBmaWxlLCAiXG4iKSkKICB9CiAgCiAgIyBEYXRhIGZpbGVzIHNhdmVkCiAgcHJvY2Vzc2VkX2ZpbGVzIDwtIGxpc3QuZmlsZXMoZmlsZS5wYXRoKGRhdGFfcGF0aCwgInByb2Nlc3NlZCIpLCBwYXR0ZXJuID0gIioucmRzfCouY3N2IiwgZnVsbC5uYW1lcyA9IEZBTFNFKQogIGNhdChwYXN0ZSgiXG5TYXZlZCIsIGxlbmd0aChwcm9jZXNzZWRfZmlsZXMpLCAicHJvY2Vzc2VkIGRhdGEgZmlsZXM6XG4iKSkKICBmb3IoZmlsZSBpbiBoZWFkKHByb2Nlc3NlZF9maWxlcywgMTApKSB7ICAjIFNob3cgZmlyc3QgMTAgdG8gYXZvaWQgY2x1dHRlcgogICAgY2F0KHBhc3RlKCIgIC0iLCBmaWxlLCAiXG4iKSkKICB9CiAgaWYobGVuZ3RoKHByb2Nlc3NlZF9maWxlcykgPiAxMCkgewogICAgY2F0KHBhc3RlKCIgIC4uLiBhbmQiLCBsZW5ndGgocHJvY2Vzc2VkX2ZpbGVzKSAtIDEwLCAibW9yZSBmaWxlc1xuIikpCiAgfQogIAogICMgS2V5IEZpbmRpbmdzIFN1bW1hcnkKICBjYXQoIlxuS0VZIEZJTkRJTkdTXG4iKQogIGNhdCgiPT09PT09PT09PT09XG4iKQogIGNhdCgiMS4gREFUQVNFVCBDSEFSQUNURVJJU1RJQ1M6XG4iKQogIGNhdCgiICAgLSBMYXJnZS1zY2FsZSBhbmFseXNpcyBvZiBHVEV4IHdob2xlLXNsaWRlIGltYWdlIGZlYXR1cmVzXG4iKQogIGNhdCgiICAgLSBDb21wcmVoZW5zaXZlIGNvdmVyYWdlIG9mIGh1bWFuIHRpc3N1ZSB0eXBlc1xuIikKICBjYXQoIiAgIC0gUXVhbGl0eSBtZXRyaWNzIGF2YWlsYWJsZSBmb3Igc3Vic3RhbnRpYWwgc3Vic2V0XG5cbiIpCiAgCiAgY2F0KCIyLiBUSVNTVUUgSEVURVJPR0VORUlUWTpcbiIpCiAgY2F0KCIgICAtIENsZWFyIHRpc3N1ZS1zcGVjaWZpYyBjbHVzdGVyaW5nIGluIFVNQVAgYW5hbHlzaXNcbiIpCiAgY2F0KCIgICAtIFZhcmlhYmxlIHNpbGhvdWV0dGUgc2NvcmVzIGluZGljYXRlIGRpZmZlcmVudCB0aXNzdWUgc2VwYXJhYmlsaXR5XG4iKQogIGNhdCgiICAgLSBGZWF0dXJlIGltcG9ydGFuY2UgdmFyaWVzIHNpZ25pZmljYW50bHkgYWNyb3NzIHRpc3N1ZSB0eXBlc1xuXG4iKQogIAogIGNhdCgiMy4gUVVBTElUWSBQUkVESUNUSU9OIE1PREVMUzpcbiIpCiAgY2F0KCIgICAtIFRpc3N1ZS1zcGVjaWZpYyBtb2RlbHMgc2hvdyBoZXRlcm9nZW5lb3VzIHBlcmZvcm1hbmNlXG4iKQogIGNhdCgiICAgLSBTb21lIHRpc3N1ZXMgaGlnaGx5IHByZWRpY3RhYmxlLCBvdGhlcnMgbW9yZSBjaGFsbGVuZ2luZ1xuIikKICBjYXQoIiAgIC0gQ3Jvc3MtdmFsaWRhdGlvbiBkZW1vbnN0cmF0ZXMgbW9kZWwgcm9idXN0bmVzc1xuXG4iKQogIAogIGNhdCgiNC4gRkVBVFVSRSBBTkFMWVNJUzpcbiIpCiAgY2F0KCIgICAtIFN0YXRpc3RpY2FsIGZlYXR1cmUgY2F0ZWdvcmllcyBzaG93IGRpZmZlcmVudCBpbXBvcnRhbmNlIHBhdHRlcm5zXG4iKQogIGNhdCgiICAgLSBQYXJ0aWFsIG92ZXJsYXAgYmV0d2VlbiBSSU4gYW5kIGF1dG9seXNpcyBwcmVkaWN0aXZlIGZlYXR1cmVzXG4iKQogIGNhdCgiICAgLSBGZWF0dXJlIGNvbnNpc3RlbmN5IHZhcmllcyBhY3Jvc3MgdGlzc3Vlc1xuXG4iKQogIAogIGNhdCgiNS4gQklPTE9HSUNBTCBJTlNJR0hUUzpcbiIpCiAgY2F0KCIgICAtIFRpc3N1ZSB0eXBlIGlzIHN0cm9uZ2VzdCBwcmVkaWN0b3Igb2YgcXVhbGl0eSB2YXJpYW5jZVxuIikKICBjYXQoIiAgIC0gQWdlIGFuZCBzZXggc2hvdyBtb2Rlc3QgYnV0IHNpZ25pZmljYW50IGVmZmVjdHNcbiIpCiAgY2F0KCIgICAtIEhhcmR5IHNjYWxlIChkZWF0aCBjaXJjdW1zdGFuY2VzKSBpbmZsdWVuY2VzIHRpc3N1ZSBxdWFsaXR5XG5cbiIpCiAgCiAgIyBUZWNobmljYWwgRGV0YWlscwogIGNhdCgiVEVDSE5JQ0FMIERFVEFJTFNcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIkFuYWx5c2lzIFBpcGVsaW5lIENvbXBvbmVudHM6XG4iKQogIGNhdCgiICAxLiBEYXRhIEludGVncmF0aW9uIGFuZCBRdWFsaXR5IENvbnRyb2xcbiIpCiAgY2F0KCIgIDIuIERlbW9ncmFwaGljIGFuZCBUaXNzdWUgRGlzdHJpYnV0aW9uIEFuYWx5c2lzXG4iKQogIGNhdCgiICAzLiBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpXG4iKQogIGNhdCgiICA0LiBVbmlmb3JtIE1hbmlmb2xkIEFwcHJveGltYXRpb24gYW5kIFByb2plY3Rpb24gKFVNQVApXG4iKQogIGNhdCgiICA1LiBTaWxob3VldHRlIEFuYWx5c2lzIGZvciBDbHVzdGVyIFZhbGlkYXRpb25cbiIpCiAgY2F0KCIgIDYuIFF1YWxpdHkgTWV0cmljcyBEaXN0cmlidXRpb24gQW5hbHlzaXNcbiIpCiAgY2F0KCIgIDcuIEFuYWx5c2lzIG9mIFZhcmlhbmNlIChBTk9WQSkgZm9yIERlbW9ncmFwaGljIEVmZmVjdHNcbiIpCiAgY2F0KCIgIDguIENvcnJlbGF0aW9uIEFuYWx5c2lzIEJldHdlZW4gUXVhbGl0eSBNZXRyaWNzXG4iKQogIGNhdCgiICA5LiBUaXNzdWUtU3BlY2lmaWMgUHJlZGljdGl2ZSBNb2RlbGluZyAoTGFzc28gUmVncmVzc2lvbilcbiIpCiAgY2F0KCIgMTAuIENyb3NzLVZhbGlkYXRpb24gYW5kIE1vZGVsIFN0YWJpbGl0eSBBc3Nlc3NtZW50XG4iKQogIGNhdCgiIDExLiBGZWF0dXJlIEltcG9ydGFuY2UgYW5kIENvbnNpc3RlbmN5IEFuYWx5c2lzXG4iKQogIGNhdCgiIDEyLiBSZXNpZHVhbCBBbmFseXNpcyBhbmQgTW9kZWwgRGlhZ25vc3RpY3NcbiIpCiAgY2F0KCIgMTMuIENvbXBhcmF0aXZlIEFuYWx5c2lzIEFjcm9zcyBUaXNzdWUgVHlwZXNcblxuIikKICAKICBjYXQoIlN0YXRpc3RpY2FsIE1ldGhvZHM6XG4iKQogIGNhdCgiICAtIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGZvciBtb2RlbCB0cmFpbmluZ1xuIikKICBjYXQoIiAgLSBMYXNzbyByZWd1bGFyaXphdGlvbiBmb3IgZmVhdHVyZSBzZWxlY3Rpb25cbiIpCiAgY2F0KCIgIC0gU3BlYXJtYW4gY29ycmVsYXRpb24gZm9yIG5vbi1wYXJhbWV0cmljIGFzc29jaWF0aW9uc1xuIikKICBjYXQoIiAgLSBBTk9WQSBmb3IgdmFyaWFuY2UgZGVjb21wb3NpdGlvblxuIikKICBjYXQoIiAgLSBTaWxob3VldHRlIGFuYWx5c2lzIGZvciBjbHVzdGVyIHF1YWxpdHkgYXNzZXNzbWVudFxuXG4iKQogIAogICMgRm9vdGVyCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVORCBPRiBSRVBPUlQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgY2F0KHBhc3RlKCJSZXBvcnQgY29tcGxldGVkOiIsIFN5cy50aW1lKCksICJcbiIpKQogIGNhdCgiRm9yIHF1ZXN0aW9ucyBvciBhZGRpdGlvbmFsIGFuYWx5c2lzLCBjb250YWN0IHRoZSBhbmFseXNpcyB0ZWFtLlxuIikKICAKICBzaW5rKCkKICAKICBtZXNzYWdlKCJDb21wcmVoZW5zaXZlIHJlcG9ydCBnZW5lcmF0ZWQgc3VjY2Vzc2Z1bGx5ISIpCiAgbWVzc2FnZSgiUmVwb3J0IHNhdmVkIHRvOiAiLCByZXBvcnRfcGF0aCkKICAKICByZXR1cm4ocmVwb3J0X3BhdGgpCn0KCiMgRXhlY3V0ZSB0aGUgY29tcHJlaGVuc2l2ZSByZXBvcnQKc3RhcnRfdGltZSA8LSBTeXMudGltZSgpICAjIFRyYWNrIGFuYWx5c2lzIGR1cmF0aW9uCmNvbXByZWhlbnNpdmVfcmVwb3J0X3BhdGggPC0gZ2VuZXJhdGVfY29tcHJlaGVuc2l2ZV9yZXBvcnQoKQpgYGAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTUuIEZpbmFsIFJlcG9ydAojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQojIEdlbmVyYXRlIGNvbXByZWhlbnNpdmUgcmVwb3J0CnJlcG9ydF9wYXRoIDwtIGZpbGUucGF0aChkYXRhX3BhdGgsICJwcm9jZXNzZWQvdGlzc3VlX21vZGVsaW5nX3JlcG9ydC50eHQiKQpzaW5rKHJlcG9ydF9wYXRoKQoKY2F0KCJHVEV4IFRJU1NVRS1MRVZFTCBNT0RFTElORyBSRVBPUlRcbiIpCmNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiIpCgpjYXQoIlNVTU1BUlk6XG4iKQpjYXQocGFzdGUoIkFuYWx5c2lzIERhdGU6IiwgU3lzLnRpbWUoKSwgIlxuIikpCmNhdChwYXN0ZSgiVG90YWwgVGlzc3VlcyBBbmFseXplZDoiLCBsZW5ndGgodW5pcXVlKG1lcmdlZF9ndGV4JHRpc3N1ZV9tYWluKSksICJcbiIpKQpjYXQocGFzdGUoIlRvdGFsIFNhbXBsZXM6IiwgbnJvdyhtZXJnZWRfZ3RleCksICJcblxuIikpCgpjYXQoIk1PREVMSU5HIFJFU1VMVFM6XG4iKQoKIyBSSU4gTW9kZWxzCmlmKCJTTVJJTiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICByaW5fbW9kZWxzIDwtIGFsbF9yZXN1bHRzW1siU01SSU4iXV0KICByaW5fcl92YWx1ZXMgPC0gc2FwcGx5KHJpbl9tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgIHJldHVybih4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKQogICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICB9KQogIHJpbl9yX3ZhbHVlcyA8LSByaW5fcl92YWx1ZXNbIWlzLm5hKHJpbl9yX3ZhbHVlcyldCiAgCiAgY2F0KCJcblJJTiBTY29yZSBQcmVkaWN0aW9uOlxuIikKICBjYXQocGFzdGUoIiAgU3VjY2Vzc2Z1bCBtb2RlbHM6IiwgbGVuZ3RoKHJpbl9yX3ZhbHVlcyksICJcbiIpKQogIGNhdChwYXN0ZSgiICBNZWFuIFI6Iiwgcm91bmQobWVhbihyaW5fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIE1lZGlhbiBSOiIsIHJvdW5kKG1lZGlhbihyaW5fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIFN0cm9uZyBjb3JyZWxhdGlvbnMgKFIgPiAwLjUpOiIsIHN1bShyaW5fcl92YWx1ZXMgPiAwLjUpLCAiXG4iKSkKfQoKIyBBdXRvbHlzaXMgTW9kZWxzCmlmKCJTTUFUU1NDUiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICBhdXRvX21vZGVscyA8LSBhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dCiAgYXV0b19yX3ZhbHVlcyA8LSBzYXBwbHkoYXV0b19tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgIHJldHVybihzcXJ0KHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkKICB9KQogIGF1dG9fcl92YWx1ZXMgPC0gYXV0b19yX3ZhbHVlc1shaXMubmEoYXV0b19yX3ZhbHVlcyldCiAgCiAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBQcmVkaWN0aW9uOlxuIikKICBjYXQocGFzdGUoIiAgU3VjY2Vzc2Z1bCBtb2RlbHM6IiwgbGVuZ3RoKGF1dG9fcl92YWx1ZXMpLCAiXG4iKSkKICBjYXQocGFzdGUoIiAgTWVhbiBSOiIsIHJvdW5kKG1lYW4oYXV0b19yX3ZhbHVlcyksIDMpLCAiXG4iKSkKICBjYXQocGFzdGUoIiAgTWVkaWFuIFI6Iiwgcm91bmQobWVkaWFuKGF1dG9fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIFN0cm9uZyBjb3JyZWxhdGlvbnMgKFIgPiAwLjUpOiIsIHN1bShhdXRvX3JfdmFsdWVzID4gMC41KSwgIlxuIikpCn0KCmNhdCgiXG5GRUFUVVJFIEFOQUxZU0lTOlxuIikKY2F0KHBhc3RlKCIgIENvbW1vbiBmZWF0dXJlcyBiZXR3ZWVuIG1vZGVsczoiLCBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzKSwgIlxuIikpCmNhdChwYXN0ZSgiICBSSU4tc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSwgIlxuIikpCmNhdChwYXN0ZSgiICBBdXRvbHlzaXMtc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKGF1dG9fb25seV9mZWF0dXJlcyksICJcbiIpKQoKY2F0KCJcbktFWSBGSU5ESU5HUzpcbiIpCmNhdCgiMS4gVGlzc3VlLXNwZWNpZmljIG1vZGVscyBzaG93IGhldGVyb2dlbmVvdXMgcHJlZGljdGFiaWxpdHlcbiIpCmNhdCgiMi4gRmVhdHVyZSBpbXBvcnRhbmNlIHZhcmllcyBhY3Jvc3MgdGlzc3VlcyBhbmQgcXVhbGl0eSBtZXRyaWNzXG4iKQpjYXQoIjMuIENyb3NzLXZhbGlkYXRpb24gZGVtb25zdHJhdGVzIG1vZGVsIHJvYnVzdG5lc3NcbiIpCgpjYXQoIlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4iKQpjYXQoIlJlcG9ydCBnZW5lcmF0ZWQgb246IiwgYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCAiXG4iKQoKc2luaygpCgptZXNzYWdlKCJBbmFseXNpcyBjb21wbGV0ZSEgUmVwb3J0IHNhdmVkIHRvOiIsIHJlcG9ydF9wYXRoKQpgYGA=